sharedworker 无法直接创建 websocket,需主控页建连并用 broadcastchannel 广播状态,由 sharedworker 统一代理通信以复用 tcp 连接;需处理连接协调、精准路由、心跳保活及 safari 兼容性降级。

SharedWorker 本身不能直接创建 WebSocket,必须靠主控页建连 + BroadcastChannel 广播状态,再由 SharedWorker 统一代理通信。这是目前唯一能真正复用单条 TCP 连接、降低服务端并发压力的方案。
主控页负责建连与状态广播
首个打开的标签页作为主控页,初始化 WebSocket,并通过 BroadcastChannel 向所有同源页面同步连接生命周期:
- 使用固定频道名,如 new BroadcastChannel('app-socket-bus'),确保所有页面和 SharedWorker 使用完全一致的字符串
- 连接成功后广播 { type: 'ws-open', id: crypto.randomUUID() };关闭时必须广播 { type: 'ws-closed' },否则 SharedWorker 可能继续向失效连接发消息
- 主控页需定期刷新本地状态(例如每 20 秒写入 localStorage 或再次广播),防止因页面崩溃或假死导致其他页面无法感知连接异常
SharedWorker 监听并协调页面请求
SharedWorker 不主动建连,而是等待广播就绪信号后才响应各页面的连接指令:
- 在 onconnect 回调开头就创建 BroadcastChannel 实例,保证监听时机早于任何页面通信
- 收到 'ws-open' 后,缓存连接可用状态;后续页面发来 { type: 'connect' } 请求时,不再新建连接,只登记端口与 clientId
- 页面关闭前应主动发送 { type: 'disconnect', clientId },SharedWorker 清理映射表和订阅关系,避免内存泄漏
精准路由与连接保活
多个页面共用一条链路,但消息不能混发。SharedWorker 必须承担路由中枢职责:
- 每个页面首次通信时生成唯一 clientId(推荐 crypto.randomUUID()),注册到 SharedWorker 的映射表中
- 服务端下发消息需携带 targetClientId 或 topic 字段,Worker 解析后仅推送给匹配 port,不广播
- 心跳与重连由 SharedWorker 单点控制:用 setInterval 发送 ping,用 setTimeout + 指数退避(1s → 2s → 4s…上限 30s)处理断线重连,所有 onmessage/onerror/onclose 回调内加 trycatch
兼容性兜底不可省略
Safari 对 SharedWorker 支持较晚(iOS 16.4+/macOS 13.3+),必须设计降级路径:
- 检测 typeof SharedWorker === 'function',不支持时 fallback 到 BroadcastChannel + 主从选举模式
- 主从选举可用 localStorage 时间戳竞拍:各页面尝试写入 ws_owner,以最新者为真主控页;失败者监听 storage 事件等待接管
- 降级后仍需保持消息路由逻辑一致——每个页面自行管理 WebSocket,但通过 BroadcastChannel 同步订阅状态与业务消息,避免重复推送











