SharedWorker 是唯一能跨标签页、iframe 和多窗口统一调度 WebSocket 心跳的机制,将心跳逻辑抽离至独立线程单点发起,避免多页面各自 setTimer 导致移动端 CPU 频繁唤醒与后台高耗电,须配合 pong 响应闭环判定、轻量 ping 帧及 Safari 兜底降级。

SharedWorker 是唯一能跨标签页、iframe 和多窗口统一调度 WebSocket 心跳的机制,把心跳逻辑从每个页面主线程中抽离出来,在独立线程里单点发起,可显著降低移动端 CPU 唤醒次数和后台耗电。关键不是“共享连接”,而是“统一心跳出口”——避免多个页面各自 setTimer 导致频繁唤醒。
心跳必须由 SharedWorker 单点控制
移动端浏览器(尤其 iOS Safari)在标签页切后台时会冻结主线程定时器,但 SharedWorker 线程仍可运行(只要同源页面至少有一个存活)。若心跳放在页面里:
- 前台页面心跳正常,后台页面定时器被暂停或大幅降频
- 用户切回时多个页面同时补发 ping,触发服务端误判或资源抖动
- 每个页面都持有一个 setInterval,CPU 频繁唤醒,加剧电池消耗
正确做法是:所有心跳逻辑写死在 shared-worker.js 中,例如:
setInterval(() => {if (ws?.readyState === WebSocket.OPEN) ws.send("ping");
}, 30000);
心跳包要轻量且协议合规
移动端网络切换频繁(WiFi ↔ 4G/5G),心跳包过大或频率过高会放大延迟与丢包风险:
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- 使用空 payload 的标准 Ping 帧(RFC 6455 允许),不附带 JSON 或时间戳等业务数据
- 心跳间隔设为 30–45 秒:太短增加唤醒负担,太长易被 NAT/防火墙断连
- 服务端必须响应 Pong 帧(非业务消息),客户端 Worker 收到后才视为有效心跳
超时判定与状态同步需闭环处理
仅发 ping 不够,Worker 必须自主判断是否“真连”:
- 用 Date.now() 记录每次 send("ping") 时间,收到 pong 时比对延迟;连续两次 pong 超过 60 秒未达,主动 close 并触发重连
- 断连期间向所有 port 广播 { type: "status", state: "disconnected" },页面 UI 可据此隐藏“在线指示器”,避免误导用户
- 重连成功后,再广播 { type: "status", state: "connected" },并自动重发各页面缓存的 subscribe 请求
兼容性兜底避免心跳失效
Safari 旧版本(macOS 13.2 / iOS 16.3 及以下)不支持 SharedWorker,此时需降级:
- 检测 self.SharedWorker 是否可用,不可用则 fallback 到 BroadcastChannel + 主控页代理模式
- 主控页(如首页或活动页)负责心跳,通过 BroadcastChannel 向其他页面广播 { type: "heartbeat-ok", ts: 1746794640 },各页面只监听不发送
- 所有页面禁用自身心跳定时器,仅依赖广播信号更新本地连接状态










