唯一稳定复用单TCP连接的方案是SharedWorker托管WebSocket+BroadcastChannel同步状态:SharedWorker负责连接创建/复用/关闭,BroadcastChannel广播连接健康状态,页面通过MessagePort注册并按clientId精准路由消息。

WebSocket 本身不支持跨标签页复用——每个 new WebSocket() 都会新建一条 TCP 连接。真正实现“单连接、多页面共享”,必须把连接收束到一个共享运行环境里,再由它统一管理、分发消息。目前唯一能稳定复用单条 TCP 连接的方案是:SharedWorker 托管 WebSocket 实例 + BroadcastChannel 同步连接状态。
SharedWorker 必须承担连接创建与维护职责
普通页面或普通 Worker 无法在后台维持 WebSocket,且 WebSocket 对象不可序列化、不能通过 postMessage 传递。只有 SharedWorker 允许:
- 在脚本中直接执行
new WebSocket(url)并长期持有实例 - 接收多个页面发来的指令(如
{ type: "connect", url: "wss://..." }),只在首次触发时建连 - 缓存连接对象,并在后续页面请求时复用;关闭时显式调用
ws.close()并清空缓存 - 所有事件回调(
onopen/onmessage/onclose/onerror)都加try...catch,防止异常导致 Worker 崩溃
BroadcastChannel 负责轻量状态广播,不传业务数据
它不是消息中转站,而是“连接健康通知器”。所有页面和 SharedWorker 必须使用完全相同的频道名(如 "app-ws-bus")初始化:
- SharedWorker 连接成功后,广播
{ type: "connected", id: crypto.randomUUID() } - 页面监听到该消息,就停止自行建连,转而向 SharedWorker 注册自己的
MessagePort - 连接异常断开或主动关闭时,必须广播
{ type: "ws-closed" },否则其他页面可能继续往失效连接发请求 - 主控页可每 20 秒广播一次心跳或写入
localStorage,避免因页面假死导致状态不同步
每个页面需带唯一 clientId 实现精准路由
服务端下发的消息不能“群发”给所有页面,SharedWorker 必须做路由中枢:
立即学习“Java免费学习笔记(深入)”;
- 页面首次通信时生成强唯一标识:优先用
crypto.randomUUID(),降级用Date.now() + Math.random() - SharedWorker 将
port与clientId映射存储(不用localStorage时间戳等弱标识) - 服务端消息必须携带
targetClientId或topic字段,Worker 才能按需分发 - 页面收到消息后,先校验 payload 中的
clientId是否匹配自身,再更新 DOM
Safari 等旧环境需降级为 localStorage 竞拍模式
SharedWorker 在 Safari 中支持不稳定,此时可退而求其次:
- 所有页面监听
storage事件,检查localStorage.ws_owner是否为空 - 为空则尝试写入自身标识并获得“Owner”身份,负责建连和广播
- 非 Owner 页面监听
storage变更,从 Owner 页面同步订阅关系与消息 - Owner 页面关闭前需清除
ws_owner,触发下一轮竞拍,避免单点故障


















