本文详解如何利用SharedWorker实现同源页面间的高效通信。通过对比localStorage方案,阐述SharedWorker复用实例及port消息机制。重点讲解页面与Worker的消息交互、调试方法,以及多页面广播和无效Port清理策略,提供完整代码示例与最佳实践。
在GitHub上浏览未登录页面并完成登录后,切换至其他页面时会看到相应的状态提示。

实现这一功能的一种常见思路是利用localStorage:在登录页面修改其状态,其他页面在显示时读取最新状态并展示提示。
// 登录的页面
localStorage.setItem('login', true);
// 其他页面
document.addEventListener("visibilitychange", function() {
if (localStorage.setItem('login') === 'true') {
alert('你已登录,请刷新页面');
}
}
然而,GitHub并未采用此方式,localStorage中也无相关字段。经排查发现,其实际使用了SharedWorker来实现该功能。

SharedWorker简介
SharedWorker是Web Worker的一种变体,允许所有同源页面共享同一个Worker实例。与普通Worker类似,只需传入JavaScript文件的URL即可注册一个SharedWorker实例。
let myWorker = new SharedWorker('worker.js');
与普通Worker不同,SharedWorker具有以下特点: 1. 相同的JS URL只会创建一个SharedWorker实例,后续相同URL的请求将复用该实例,实现多页面共享。 2. 页面与SharedWorker之间通过port(端口)来发送和接收消息。
接下来,我们将具体探讨页面与Worker之间如何发送和接收消息。
messagePort机制
假设存在两个JS文件:运行在页面中的page.js和运行在Worker中的worker.js。在page.js中注册SharedWorker的代码如下:
// page.js
let myWorker = new SharedWorker('worker.js');
// page通过worker port发送消息
myWorker.port.postMessage('哼哼');
// page通过worker port接收消息
myWorker.port.onmessage = (e) => console.log(e.data);
// worker.js
onconnect= function(e) {
const port = e.ports[0];
port.postMessage('哈嘿');
port.onmessage = (e) => {
console.log(e.data);
}
}
调试SharedWorker
在上述示例中,若Worker中使用console.log打印来自页面的消息,可通过浏览器地址栏输入chrome://inspect,在侧边栏选中Shared Workers查看当前运行的所有Worker。点击Inspect可打开开发者工具,从而查看输出的日志。

示例中Worker名称显示为untitled,这是因为SharedWorker构造函数支持传入第二个参数作为名称。
let myWorker = new SharedWorker('worker.js', 'awesome worker');
多页面消息广播
回到文章开头的例子,前文实现了页面与Worker间的通信。若需让Worker向多个页面发送消息,可将port缓存起来,构建一个port池(port pool)。当需要向所有页面广播消息时,遍历该池并发送消息即可。
// worker js
const portPool = [];
onconnect= function(e) {
const port = e.ports[0];
// 在connect时将 port添加到 portPool中
portPool.push(port);
port.postMessage('哈嘿');
port.onmessage = (e) => {
console.log(e.data);
}
}
function boardcast(message) {
portPool.forEach(port => {
port.portMessage(port);
})
}
通过上述方式,我们基本实现了向多个页面广播消息的功能。
清除无效Port
上述实现存在一个问题:页面关闭后,portPool中的port不会自动清除,导致内存浪费。因此,可在页面关闭前通知SharedWorker即将关闭,由Worker将无效的messagePort从portPool中移除。
// 页面
window.onbeforeunload = () => {
myWorker.port.postMessage('TO BE CLOSED');
};
// worker.js
const portPool = [];
onconnect = function(e) {
var port = e.ports[0];
portPool.push(port);
port.onmessage = function(e) {
console.log(e);
if (e.data === 'TO BE CLOSED') {
const index = ports.findIndex(p => p === port);
portPool.splice(index, 1);
}
var workerResult = 'Result: ' + (e.data[0] * e.data[1]);
port.postMessage(workerResult);
}
}
function boardcast(message) {
portPool.forEach(port => {
port.portMessage(port);
})
}
至此,我们实现了一个简单的多页面广播SharedWorker。例如,可用于广播当前时间。
setInterval(() => boardcast(Date.now()), 1000);
参考资料
https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker/SharedWorker https://github.com/mdn/simple-shared-worker
本文介绍了JavaScript中SharedWorker实现多页面通信的完整过程。更多相关JS SharedWorker多页面通信内容,可搜索本站历史文章或继续浏览后续相关文章。