BroadcastChannel 与 SharedWorker 本质不互通,因通信链路断裂、生命周期错位、跨源限制三重问题;SharedWorker 默认不监听 BroadcastChannel 的 message 事件,需自行创建同名通道并转发消息。
BroadcastChannel
和
SharedWorker
本质不互通,强行“配合”会掉进通信链路断裂、生命周期错位、跨源限制三重坑里——别这么干。
为什么 BroadcastChannel.postMessage() 发不到 SharedWorker
只能向同源、同
的
、
(含
)、
实例广播,但
的全局作用域(
)**默认不监听
事件以外的通道消息**。它收不到
的事件,因为:
发送的是
事件,但触发在
上,而
的主入口脚本需显式调用
才能接收 —— 这个
是来自页面的
,不是
的事件对象类型是
,但它的
是
,无法被
的
关联或响应
实测中,即使在
中
并
,也收不到其他 tab 发来的消息 —— 因为各
实例彼此隔离,不共享事件循环
想让 SharedWorker 当“中心”,得换通信路径
真正可行的方案是:所有页面通过
的
发送消息,由
统一处理并用
向全站广播;反过来,若某页要用
主动推,
必须自己也创建同名
并监听,再把消息转发到各
。关键点:
每个页面必须先
,再
,否则
不激活
内部要同时维护两个通信端:一个
(用于接收广播),一个
存所有连接的
(用于反向分发)
的
不能传
、
节点、
或带循环引用的对象;建议只传 plain object +
/
如果页面关闭但没
,
的
会滞留,需监听
清理
示例片段(SharedWorker 主脚本):
兼容性与降级必须考虑的硬伤
在 Safari 15.4+ 才支持
实例监听(此前仅限
),且 iOS Safari 完全不支持
;
本身在 Firefox 中默认禁用(需
开启),Edge 105+ 才稳定。这意味着:
不能假设
一定存在,需用
包裹初始化逻辑
一旦降级,只能 fallback 到
+
事件(延迟高、不支持二进制、无顺序保证),或直接用多个
实例各自广播(失去“中心”调度能力)
的
必须显式调用,否则可能泄漏内存;尤其在 SPA 路由切换时,容易忘记清理旧 channel 实例
真正可靠的“全站消息中心”目前只有
+
+
这一条路;
的定位是多页面共享状态和轻量计算,不是通信枢纽。把广播逻辑塞进它里面,等于让快递员兼做调度中心和分拣线——活能干,但一出错就全瘫。
BroadcastChannelnameWindowWorkerServiceWorkerSharedWorkerSharedWorkerselfmessageBroadcastChannelBroadcastChannelmessageselfSharedWorkerself.addEventListener('message', ...)messageport.postMessage()BroadcastChannelBroadcastChannelMessageEventsourcenullSharedWorkerportSharedWorkernew BroadcastChannel('xxx')addEventListener('message', ...)BroadcastChannelSharedWorkerportSharedWorkerBroadcastChannelBroadcastChannelSharedWorkerBroadcastChannelportconst sw = new SharedWorker(...)sw.port.start()portSharedWorkerBroadcastChannel('my-channel')MapportBroadcastChannelpostMessage()functionDOMundefinedArrayBufferTransferableport.close()SharedWorkerportport.onclose
const bc = new BroadcastChannel('app-bus');
const ports = new Map();
self.addEventListener('connect', e => {
const port = e.ports[0];
const id = crypto.randomUUID();
ports.set(id, port);
port.start();
port.onmessage = ({ data }) => {
// 页面主动发给 SharedWorker 的指令
if (data.type === 'broadcast') {
bc.postMessage({ from: id, payload: data.payload });
}
};
port.onclose = () => ports.delete(id);
});
bc.addEventListener('message', e => {
// 收到广播,转发给所有活跃 port
ports.forEach(port => port.postMessage(e.data));
});
BroadcastChannelSharedWorkerWindowSharedWorkerSharedWorkerdom.serviceWorkers.enabledSharedWorkerif (typeof SharedWorker !== 'undefined')localStoragestorageBroadcastChannelBroadcastChannelclose()ServiceWorkerclients.matchAll()postMessage()SharedWorker