sharedworker 是实现多页面共享单个 websocket/mqtt 连接的必要机制,连接创建、重连、心跳、销毁均须在 worker 内完成,页面仅通过 postmessage 控制并收发消息。

SharedWorker 是目前浏览器中唯一能天然支持多窗口共用单个长连接的机制。它不是“辅助方案”,而是实现全局唯一 WebSocket 或 MQTT 连接的必要载体——连接必须在 SharedWorker 内部创建、重连、心跳和销毁,页面只负责发指令、收消息。
连接必须完全托管在 SharedWorker 内
页面无法把 WebSocket 实例传给 SharedWorker(postMessage 会报 DataCloneError),也不能让每个页面自己连一次(导致服务端连接数暴增、频控触发)。正确做法是:
- SharedWorker 脚本中直接执行 new WebSocket(url),并缓存实例到全局变量(如
self.ws) - 所有连接逻辑——包括 onopen/onerror/onclose 回调、重连定时器、心跳发送——全部写在 worker 内
- 页面仅通过
port.postMessage({ type: 'connect', url: 'wss://...' })触发连接动作,不参与底层细节
必须处理端口生命周期与活跃状态
SharedWorker 不会自动感知页面是否关闭或崩溃。若不管理端口,容易残留无效引用,导致消息发不出或内存泄漏:
- 在
self.addEventListener('connect', ...)中保存每个 port,并为每个 port 绑定独立的onmessage处理器 - 监听 port 的
onclose(或捕获disconnect事件),及时从活跃端口列表中移除 - 当端口列表为空时,主动
ws.close()并清空定时器,避免连接空转
消息分发要带来源标识,避免响应错乱
SharedWorker 收到多个页面发来的 send 请求时,无法默认知道该把响应还给谁。不加区分会导致消息错配:
- 页面首次连接时,生成唯一 clientId(推荐
crypto.randomUUID()),随{ type: 'init', clientId }发给 worker - worker 将 clientId 与 port 关联存储(如
portMap.set(clientId, port)) - 转发服务端消息时,在 payload 中带上
from: clientId;页面根据此字段决定是否更新本地状态
重连与保活必须由 worker 主动控制
页面刷新或崩溃后,只要还有其他标签页存活,SharedWorker 就持续运行。此时连接不应中断,而应自动恢复:
- 使用 指数退避重连:初始延迟 1s,失败后翻倍(2s → 4s → 8s…上限 30s)
- 心跳检测不能只靠
ws.onmessage:需setInterval主动发 ping,连续 2 次未收到 pong 则ws.close()并触发重连 - 每次重连前检查
ws?.readyState === WebSocket.CLOSED,防止重复 new WebSocket - 所有 ws 回调内包裹
try...catch,避免未捕获异常导致整个 SharedWorker 崩溃退出











