SharedWorker 是唯一能实现全站 WebSocket 连接复用的机制,其核心是让同源多页面共用一个 TCP 连接出口,由 Worker 独立管理连接生命周期、消息路由与状态同步,页面仅发送指令并接收响应。

用 SharedWorker 维持全站唯一的 WebSocket 通道,核心不是“共享连接对象”,而是让所有页面共用一个 TCP 连接出口——连接创建、保活、重连、消息路由全部由 SharedWorker 独立完成,页面只发指令、收响应。这样既避免每个标签页重复建连导致服务端连接数暴增,也防止页面刷新或崩溃中断通信。
SharedWorker 是唯一能真正复用连接的机制
普通 Worker 每开一个标签页就新建一个实例;Service Worker 无法主动维持长连接,只响应网络请求;只有 SharedWorker 被设计为同源多上下文(tab/iframe/window)共用同一执行环境。只要还有一个同源页面打开,Worker 就持续运行,WebSocket 实例就能保持。
- 所有页面必须用完全一致的路径创建实例:
new SharedWorker('./ws-manager.js')(协议、域名、端口、文件名缺一不可) - 必须运行在 HTTPS 或
localhost下;HTTP 域名即使同源也会被静默拒绝,file://协议完全不可用 - Safari 仅支持 macOS 13.3+ / iOS 16.4+,旧版本需降级为 BroadcastChannel + 主控页代理方案
WebSocket 必须在 Worker 内创建并独占管理
WebSocket 对象不可序列化,也不能跨上下文传递。页面中 new WebSocket() 后试图 postMessage 给 SharedWorker 会直接报 DataCloneError。正确做法是把整个连接生命周期下沉到 Worker 脚本中。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- Worker 脚本(如
ws-manager.js)内直接调用new WebSocket(url),并缓存到self.ws - 页面只发送控制指令,例如:
{ type: "connect", url: "wss://api.example.com/ws" } - Worker 收到后检查
ws?.readyState,仅当为CLOSED或未初始化时才新建实例 - 连接成功后,Worker 主动广播
{ type: "connected" }给所有已注册端口
消息必须通过 clientId 精准路由,不能广播
单连接不等于单业务流。多个窗口共用一条链路,但 A 页面订阅的订单更新、B 页面收到的聊天消息必须隔离,否则会出现数据错乱。
- 每个页面首次通信时生成唯一标识:
const clientId = crypto.randomUUID() - Worker 用
Map存储clientId → port映射关系 - 页面发消息必须携带
clientId,例如:{ type: "send", data: {}, clientId: "win-abc123" } - 服务端返回的消息也应含
targetClientId或topic字段,Worker 查表后只转发给匹配的port - 页面关闭前主动发
{ type: "disconnect", clientId: "win-abc123" },Worker 清理映射与订阅关系
重连、心跳、状态同步必须由 Worker 全权负责
如果把重连逻辑放在页面里,多个标签页可能同时触发重连,造成服务端雪崩;切后台后主线程定时器也会失效。所有保活动作必须下沉到 Worker 内部。
- 使用
setInterval主动发送"ping",服务端需响应"pong";连续两次无响应即触发断连重试 - 断连后执行指数退避重连(1s → 2s → 4s → … 最大 30s),用
setTimeout实现,避免setInterval积压 - 重连成功后,自动重发各窗口缓存的
subscribe请求(需在 Worker 内维护订阅快照) - 通过
port.postMessage({ type: "status", state: "connecting" })向所有页面同步连接状态,UI 可据此更新指示器 - 所有
ws.onopen/onmessage/onerror回调内必须加try...catch,防止未捕获异常导致整个 Worker 崩溃退出

















