SharedWorker?多頁面相互通信示例詳解
正文
SharedWorker 是一個新的Web Worker API,它允許你在多個頁面之間共享一個Worker。
SharedWorker 代表一種特定類型的Worker,可以在多個瀏覽器上下文中運行,比如多個頁面或者多個iframe。
什么是 SharedWorker
根據(jù)前幾篇的了解,大家應(yīng)該對Worker有了一定的了解,SharedWorker是Worker的一種,它允許你在多個頁面之間共享一個Worker。
SharedWorker對比于Service Worker要簡單很多,同樣也是繼承與Worker,但是它的生命周期是獨立于頁面的。
SharedWorker和其他Worker的相同,都是需要同源的,它是通過url來區(qū)分是否是同一個SharedWorker。
使用 SharedWorker
SharedWorker的使用很簡單,只需要在創(chuàng)建Worker的時候,需要傳入一個name參數(shù),這個name參數(shù)就是SharedWorker的標(biāo)識,相同的name會共享同一個SharedWorker。
const sharedWorker = new SharedWorker('worker.js', 'mySharedWorker');
默認(rèn)情況下name并不是必須的,這個時候相同的url會共享同一個SharedWorker。
const sharedWorker = new SharedWorker('worker.js');
創(chuàng)建SharedWorker之后,我們就可以通過port屬性來和SharedWorker進(jìn)行通信了。
sharedWorker.port.onmessage = (e) => {
console.log(e.data);
};
在主線程中,可以通過創(chuàng)建出來的worker對象中的port屬性來和SharedWorker進(jìn)行通信。
使用worker.port.onmessage來監(jiān)聽SharedWorker的消息。
在SharedWorker中,可以通過self.onconnect來監(jiān)聽SharedWorker的連接。
在監(jiān)聽事件中會獲取到與SharedWorker連接的port,通過這個port就可以和主線程進(jìn)行通信了。
self.onconnect = (e) => {
const port = e.ports[0];
port.postMessage('hello');
};
通信還是通過postMessage來進(jìn)行的。
通信
上面的通信只是一個簡單的示例,在主線程中創(chuàng)建的SharedWorker,并使用port來和SharedWorker進(jìn)行通信。
通常情況下,我們會使用onmessage來監(jiān)聽SharedWorker的消息,使用這種方式默認(rèn)是打開狀態(tài),如果使用addEventListener來監(jiān)聽SharedWorker的消息,需要手動調(diào)用start方法來開啟。
sharedWorker.port.addEventListener('message', (e) => {
console.log(e.data);
});
sharedWorker.port.start();
在主線程中,我們可以通過port來實現(xiàn)和SharedWorker的通信。
// 發(fā)送消息
sharedWorker.port.postMessage('hello');
// 監(jiān)聽消息
sharedWorker.port.onmessage = (e) => {
console.log(e.data);
};
// 或者
sharedWorker.port.addEventListener('message', (e) => {
console.log(e.data);
});
sharedWorker.port.start();
在SharedWorker中使用方式也是一樣的,不同的是,我們需要通過self.onconnect來監(jiān)聽SharedWorker的連接。
self.onconnect = (e) => {
const port = e.ports[0];
// 發(fā)送消息
port.postMessage('hello');
// 監(jiān)聽消息
port.onmessage = (e) => {
console.log(e.data);
};
// 或者
port.addEventListener('message', (e) => {
console.log(e.data);
});
port.start();
};
可以看到上面監(jiān)聽的時候,獲取port是一個數(shù)組,這是因為SharedWorker可以和多個頁面進(jìn)行通信。
每個頁面都會有一個port,通過這個port和SharedWorker進(jìn)行通信。
通常情況下我們會記錄下所有的port,然后在需要的時候進(jìn)行通信。
const ports = [];
self.onconnect = (e) => {
const port = e.ports[0];
ports.push(port);
port.onmessage = (e) => {
console.log(e.data);
};
};
// 發(fā)送消息
function sendMessage(message) {
ports.forEach((port) => {
port.postMessage(message);
});
}
上面我們的案例中,我們只創(chuàng)建了一個SharedWorker,所以只有一個port,但是如果我們創(chuàng)建了多個SharedWorker,那么就會有多個port。
這里收到消息之后瀏覽器控制臺沒有任何打印信息,因為SharedWorker的作用域中沒有window對象,所以console、alert等方法都是無法使用的。
如果我們需要調(diào)試SharedWorker,可以在瀏覽器地址欄中輸入chrome://inspect/#workers,這樣就可以看到當(dāng)前頁面中的SharedWorker。

指定頁面通信
上面我們拿到了所有的port,但是我們并不知道這個port是哪個頁面的,所以我們需要在創(chuàng)建SharedWorker的時候,指定每個頁面的標(biāo)識。
但是很遺憾,SharedWorker并沒有提供這樣的功能,所以我們需要自己實現(xiàn)。
這里我們可以使用雙數(shù)組來實現(xiàn),一個數(shù)組用來存儲頁面的標(biāo)識,一個數(shù)組用來存儲port。
const pages = [];
const ports = [];
self.onconnect = (e) => {
const port = e.ports[0];
ports.push(port);
// 獲取頁面標(biāo)識
const page = Math.random().toString(36).substr(2);
port.postMessage(page);
pages.push(page);
// 監(jiān)聽消息
port.onmessage = (e) => {
const index = ports.indexOf(port);
const page = pages[index];
// 這樣就可以知道是哪個頁面發(fā)來的消息了
console.log(page);
};
};
有了上面的代碼,我們就可以知道是哪個頁面發(fā)來的消息了,并且也可以指定頁面發(fā)送消息。
實戰(zhàn)
上面我們已經(jīng)知道了如何使用SharedWorker,那么我們就可以使用它來實現(xiàn)一個簡單的聊天室。
這里我們使用SharedWorker來實現(xiàn)消息的轉(zhuǎn)發(fā),頁面之間的通信使用postMessage。
- SharedWorker
const uuids = [];
const ports = [];
self.onconnect = (e) => {
const port = e.ports[0];
ports.push(port);
// 獲取頁面標(biāo)識
const uuid = Math.random().toString(36).substr(2);
uuids.push(uuid);
port.postMessage({
type: 'connect',
uuid: uuid
});
// 監(jiān)聽消息
port.onmessage = (e) => {
const index = ports.indexOf(port);
const uuid = uuids[index];
// 群發(fā)消息
broadcast({
type: 'message',
sender: uuid,
message: e.data.message
});
};
};
// 群發(fā)消息
const broadcast = (data) => {
ports.forEach((port) => {
port.postMessage(data);
});
}
- index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.chat {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
#messages {
height: 500px;
width: 300px;
overflow: auto;
border: #3498db 1px solid;
padding: 0 10px;
}
.chat__messages {
flex: 1;
overflow-y: auto;
margin: 4px 0;
}
.sender {
font-size: 12px;
margin: 0;
padding: 0;
}
.sender:after {
content: ' :';
}
.message {
width: auto;
max-width: 300px;
background: #3498db;
padding: 10px;
border-radius: 10px;
margin: 10px 0;
font-size: 16px;
color: #fff;
}
</style>
</head>
<body>
<div class="chat">
<div id="messages">
</div>
<div class="chat__input">
<input type="text" id="message">
<button id="send">發(fā)送</button>
</div>
</div>
<script>
let uuid = ''
const sharedWorker = new SharedWorker('sharedWorker.js');
sharedWorker.port.onmessage = (e) => {
if (e.data.type === 'connect') {
uuid = e.data.uuid;
alert('連接成功')
return;
}
const messageDom = `
<div class="chat__messages">
<p class="sender" ${uuid === e.data.sender ? 'style="color: #165DFF"' : ""}>${e.data.sender}</p>
<p class="message">${e.data.message}</p>
</div>
`
document.getElementById('messages').insertAdjacentHTML('beforeend', messageDom);
// 滾動到底部
document.getElementById('messages').scrollTop = document.getElementById('messages').scrollHeight;
};
document.getElementById('send').addEventListener('click', () => {
const message = document.getElementById('message').value;
if (!message) {
alert('請輸入內(nèi)容')
return;
}
const data = {
type: 'message',
message: message,
}
sharedWorker.port.postMessage(data);
document.getElementById('message').value = '';
});
</script>
</body>
</html>
上面的代碼我們實現(xiàn)了一個簡單的聊天室,我們可以在多個頁面打開聊天室,然后在其中一個頁面發(fā)送消息,其他頁面都會收到消息。
上面的代碼中還有一個uuid,=還可以基于這么來實現(xiàn)私聊,比如我們可以在頁面中添加一個select,然后選擇一個用戶,然后在發(fā)送消息的時候,將uuid傳遞過去,然后在SharedWorker中判斷,如果是私聊,就只發(fā)送給指定的用戶。
這一塊就留給感興趣的同學(xué)自己實現(xiàn)了。
SharedWorker 的關(guān)閉
上面我們實現(xiàn)了一個簡單的聊天室,但是我們發(fā)現(xiàn),當(dāng)我們關(guān)閉頁面的時候,SharedWorker并沒有關(guān)閉,這是因為SharedWorker是一個長連接,當(dāng)我們關(guān)閉頁面的時候,SharedWorker并沒有關(guān)閉,所以我們需要在頁面關(guān)閉的時候,手動關(guān)閉SharedWorker。
// 關(guān)閉頁面的時候,關(guān)閉 SharedWorker
window.addEventListener('beforeunload', () => {
sharedWorker.port.postMessage({
type: 'close',
uuid: uuid
});
});
// SharedWorker.js
/** 省略其他代碼 **/
onconnect = (e) => {
const port = e.ports[0];
port.onmessage = (e) => {
if (e.data.type === 'close') {
const index = ports.indexOf(port);
ports.splice(index, 1);
return;
}
};
};
上面的代碼中,我們在頁面關(guān)閉的時候,發(fā)送一個close的消息,然后在SharedWorker中,將port從ports中移除,這樣就可以關(guān)閉SharedWorker了。
SharedWorker 的生命周期
SharedWorker的生命周期沒有那么復(fù)雜,不像ServiceWorker有那么多的過程,它只有一條規(guī)則,就是當(dāng)所有創(chuàng)建SharedWorker的頁面關(guān)閉之后,那么SharedWorker的生命就走到了盡頭,否則它就會一直常駐。
SharedWorker 還可以做什么?
聊天室只是一個小玩具,我們還可以做很多事情,我們最常見的就是跨頁面登錄,例如GitHub,我們在一個頁面登錄,然后在其他頁面就可以獲取到登錄信息,這就是SharedWorker的一個應(yīng)用場景。
還有一個應(yīng)用場景就是,我們可以在一個頁面中,監(jiān)聽一個WebSocket,然后在其他頁面中,發(fā)送消息,這樣就可以實現(xiàn)跨頁面的WebSocket。
其實任何跨頁面的通信,都可以使用SharedWorker來實現(xiàn),但是SharedWorker的缺點也很明顯,就是不支持IE,所以如果你的項目需要兼容IE,那么就不要使用SharedWorker了。
優(yōu)點很明確,就是可以跨頁面通信,缺點也很明顯,就是瀏覽器兼容確實有點捉急。

總結(jié)
本文主要介紹了SharedWorker的基本使用,并使用SharedWorker實現(xiàn)了一個簡單的聊天室。
SharedWorker的使用還是比較簡單的,不同于Worker那么單純,SharedWorker是一個長連接,所以我們可以在多個頁面中,共享一個SharedWorker,這樣就可以實現(xiàn)跨頁面通信了。
不同于ServiceWorker有那么多的限制,SharedWorker除了兼容性其實功能非常強大,且在實際項目中的應(yīng)用場景也更加廣泛。
以上就是SharedWorker 多頁面相互通信示例詳解的詳細(xì)內(nèi)容,更多關(guān)于SharedWorker 多個頁面通信的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
javascript 支持ie和firefox杰奇翻頁函數(shù)
杰奇小說系統(tǒng)用到的翻頁函數(shù),支持firefox,官方自帶的模板不支持,這樣大家就可以用firefox瀏覽網(wǎng)頁了,在網(wǎng)頁木馬橫行的今天,firefox比ie要安全不少2008-07-07
使用JavaScript操作Visual Viewport的方法示例
在現(xiàn)代前端開發(fā)中,視口(viewport)是一個非常重要的概念,它決定了用戶在瀏覽網(wǎng)頁時所看到的內(nèi)容,JavaScript 提供了一個強大的接口 —— Visual Viewport API,讓開發(fā)者可以更靈活地控制和獲取視口的信息,本文將詳細(xì)介紹如何使用 Visual Viewport API2024-09-09
如何在指定的地方插入html內(nèi)容和文本內(nèi)容
本文為大家介紹個小技巧可以在指定的地方插入html內(nèi)容和文本內(nèi)容,示例如下,感興趣的朋友可以參考下2013-12-12
利用Axios實現(xiàn)無感知雙Token刷新的詳細(xì)教程
在現(xiàn)代系統(tǒng)中,Token認(rèn)證已成為保障用戶安全的標(biāo)準(zhǔn)做法,然而,盡管許多系統(tǒng)采用了這種認(rèn)證方式,卻在處理Token刷新方面存在不足,導(dǎo)致用戶體驗不佳,許多系統(tǒng)未能提供一種無縫的、用戶無感知的Token刷新機制,所以本文介紹了教你用Axios實現(xiàn)無感知雙Token刷新2024-08-08

