sharedworker 是浏览器中唯一能跨标签页统一维护 websocket 心跳的机制,它将心跳逻辑抽离至独立共享线程,确保页面失焦、崩溃等场景下心跳持续运行,并通过状态广播与连接复用实现高效保活。

SharedWorker 是目前浏览器中唯一能跨标签页统一维护 WebSocket 心跳的机制。它把心跳逻辑完全抽离出页面主线程,由一个独立、共享的后台线程持续运行——只要还有一个同源页面开着,心跳就不断,连接就不掉,彻底避免每个标签页各自唤醒网络栈造成的冗余开销。
心跳必须完全下沉到 SharedWorker 内部
页面切后台、失焦、执行缓慢甚至崩溃时,主线程定时器(如 setInterval)会暂停或失效,导致心跳中断、连接被服务端误判为离线。SharedWorker 不受这些影响,它的定时器始终运行:
- 使用
setInterval(() => ws.send("ping"), 30000)发送心跳,不依赖任何页面活跃状态 - 服务端需明确响应
"pong";Worker 内记录每次收到 pong 的时间戳 - 若连续两次未在超时窗口(如 45 秒)内收到 pong,主动调用
ws.close()并触发重连流程 - 所有
onopen/onmessage/onerror回调里加try...catch,防止未捕获异常导致 Worker 整体退出
单点心跳 + 多端状态同步,避免雪崩
多个标签页若各自发起心跳,不仅浪费带宽,还可能因服务端频控或连接复位引发连锁断连。SharedWorker 作为唯一心跳源,同时承担状态广播职责:
- 心跳成功、重连中、已断开等状态,通过
port.postMessage({ type: "status", state: "connected" })推送给所有已连接页面 - 页面 UI 根据状态更新连接指示器(如小圆点颜色),不自行判断或轮询
- 禁止页面在
beforeunload或visibilitychange中尝试发心跳或重连指令——这些动作全交由 Worker 统一调度
心跳保活与连接复用强绑定
心跳不是孤立功能,必须和连接生命周期、消息路由深度耦合:
- WebSocket 实例必须在 SharedWorker 脚本内创建,且仅初始化一次;重复
new WebSocket()前先检查ws?.readyState === WebSocket.CLOSED - 心跳包本身需携带轻量上下文(如
{"type":"ping","ts":1745786820123}),便于服务端做连接健康度统计 - 服务端返回的业务消息中,若含
"keepalive_ack"字段,Worker 可视作一次有效心跳响应,延长下次检测窗口 - 页面关闭时发送
{ type: "disconnect", clientId: "tab-xyz" },Worker 清理对应映射,但不关闭连接——其他标签页仍在使用
不复杂但容易忽略:心跳不是“让连接不断”,而是“让连接有意义”。SharedWorker 把心跳从页面负担变成基础设施,既省了客户端 CPU 和网络唤醒次数,也帮服务端过滤掉了大量无效连接请求。











