SharedWorker 通过全局唯一 WebSocket 连接实现最省电路径:所有页面共用单条 TCP 连接,由 Worker 独占管理生命周期、统一心跳(30秒单点ping)、指数退避重连,并基于 clientId 精准路由消息,彻底消除多连接唤醒耗电。

SharedWorker 本身不直接提供“连接池”,但它能实现全局唯一长连接,这在移动端正是降低功耗最有效的路径——不是靠多连复用,而是靠彻底消灭冗余连接。频繁建连、心跳、重试才是耗电大户,而 SharedWorker 让所有标签页共用一条 WebSocket,从源头掐断了重复网络激活。
为什么单连接比连接池更省电
移动端无线模块(蜂窝/WiFi)的功耗峰值集中在“唤醒→建连→收发→休眠”全过程。每多一个 WebSocket 实例,就多一次完整唤醒周期。实测表明:每 15 秒一次独立心跳,会让射频模块持续高功耗运行;而单连接+统一心跳,可将唤醒频率压到最低。SharedWorker 的优势不在“池化”,而在“归一”——所有页面的网络请求,最终都收敛到同一个 TCP 连接上发出。
- 避免多个页面各自发起 WebSocket 握手(每次握手约 3–5 个 RTT,触发射频全功率唤醒)
- 心跳由 Worker 单点发送(如每 30 秒 ping 一次),而非每个页面独立 ping
- 重连采用指数退避(1s → 2s → 4s…),防止多窗口同时重连引发雪崩式网络请求
必须由 SharedWorker 自行创建并独占管理连接
页面不能 new WebSocket 后传给 Worker——WebSocket 对象不可序列化,postMessage 会直接抛 DataCloneError。正确做法是:所有连接逻辑下沉到 worker 脚本中,由它全权控制生命周期。
- worker 内用 new WebSocket(url) 创建实例,仅执行一次
- 页面只发指令,例如
{ type: "send", data: { action: "subscribe", topic: "order/123" }, clientId: "win-abc123" } - 连接成功后,Worker 主动广播
{ type: "connected" },页面据此更新 UI 状态 - 页面关闭前必须发
{ type: "disconnect", clientId },Worker 清理映射和订阅,避免内存泄漏
精准路由 + 按需唤醒,避免无效广播耗电
单连接不等于单业务流。若服务端消息无差别广播,每个页面都要解析、过滤、丢弃非目标消息,白白消耗 CPU 和电量。必须靠 clientId 实现端到端隔离。
- 页面加载时生成唯一标识:
const clientId = self.crypto.randomUUID() - Worker 用
Map<port clientid></port>维护映射关系 - 服务端返回消息必须带
targetClientId或topic字段,Worker 查表后只向匹配 port 转发 - 禁止
ports.forEach(port => port.postMessage())全局广播,尤其在后台标签页中会白耗 CPU
兼容性与降级策略,保障真实场景可用
Safari 对 SharedWorker 支持较晚(macOS 13.3+/iOS 16.4+),旧版本需降级。但降级方案也必须以“减少连接数”为目标,而非简单回退到每页独立连接。
- 检测
typeof SharedWorker === "function",不支持时启用 BroadcastChannel + 主控页代理模式 - 主控页(首个打开的 tab)负责建连和转发,其余页面通过 BroadcastChannel 发送指令、接收响应
- 主控页关闭时,自动选举新主控(用 localStorage + 时间戳协调),避免连接中断
- 所有环境强制使用 HTTPS,http:// 或 file:// 下 SharedWorker 会被静默禁用

















