移动端切后台或锁屏后WebSocket断开是系统级限制所致,需采用“秒级感知+可靠重建+状态兜底”策略:监听visibilitychange/pagehide事件及时响应,停心跳但不主动close;用时间戳替代readyState判断连接有效性,结合指数退避重连;本地缓存离线消息并服务端幂等校验,PWA中可借Service Worker暂存消息。

移动端切后台或锁屏后WebSocket断开,不是你代码写错了,而是iOS和Android系统主动掐断的——它不给你留连接,也不报错,ws.readyState可能还卡在1(OPEN),但发包已无效。必须放弃“保持连接”幻想,转向“秒级感知 + 可靠重建 + 状态兜底”。
监听 visibilitychange 和 pagehide 事件做状态切换响应
这是前端能做的第一道防线,比等onclose更早发现问题。iOS WKWebView、Android Chrome WebView、PWA都支持,但行为有差异:
-
visibilitychange触发时机较准:切后台/锁屏时触发document.visibilityState === 'hidden';唤醒/切回前台时触发'visible' -
pagehide在 iOS Safari 和部分 Android 浏览器中更可靠,尤其锁屏场景;event.persisted为true表示页面被缓存(如快速切回),此时连接大概率已断 - 不要在
hidden时调用ws.close()——系统可能早已断开,再调会抛InvalidStateError - 建议同时监听两个事件,并用
setTimeout防抖:比如hidden后 500ms 再停心跳,避免误判短暂滚动遮挡
重连不能只看 readyState,要叠加心跳超时验证
ws.readyState === WebSocket.OPEN 在移动端完全不可信。Android WebView 常出现“假连”:状态是1,但send()静默失败;iOS WKWebView 切后台后readyState可能数分钟不更新。
开发者平台元数据——GitHub、Docker Hub、crates.io 等生态系统注册表。使用此技能的时机:1. 解析 GitHub 仓库或获取其...
- 自己维护一个
lastHeartbeatAt时间戳,每次收到服务端心跳响应就更新 - 前端定时器每 15s 发一次心跳,若距
lastHeartbeatAt超过 45s,直接判定失效,触发重连 - 重连用指数退避:
[1000, 2000, 4000, 8000, 16000],到第 5 次后固定 30s 间隔,避免网络未恢复时刷屏建连 - 每次重连前先清空旧
ws引用,防止内存泄漏和事件重复绑定
离线消息必须本地缓存 + 服务端幂等校验
用户切后台期间发的消息,不能等重连成功再发——那可能已错过业务窗口(比如支付确认、协作编辑提交)。
- 所有关键操作(含
send())前,先存入localStorage或IndexedDB,标记status: "pending" - 重连成功后,按时间戳顺序重发
pending消息,每条附带唯一messageId - 服务端必须对
messageId做幂等处理:收到重复messageId直接返回成功,不二次执行 - 避免用
sessionStorage——切后台后可能被回收;也别依赖beforeunload,移动端基本不触发
PWA 场景下用 Service Worker 补离线能力,但别指望它保活
如果你走的是 PWA 路线,Service Worker 能帮你做两件事:缓存握手请求、暂存待发消息。但它本身受系统限制,无法长期驻留。
- 在 SW 的
install阶段预缓存/auth/token等鉴权接口,确保重连时能快速拿新 token - 连接断开时,把
pending消息写入caches.open('ws-pending'),用Cache.put()存为Request/Response对 - 监听
sync事件,在网络恢复后遍历缓存重发;注意sync不保证立即触发,iOS 上延迟可能达数分钟 - 别在 SW 里尝试维持 WebSocket 连接——SW 没有
WebSocket构造函数,且生命周期由浏览器控制,不可预测
最易被忽略的一点:服务端必须配合维护“最后心跳时间”,不能只靠前端判断。否则用户切后台 10 分钟后重连,服务端仍认为连接有效,导致新消息不推、旧会话不清理。前后端状态同步,才是长连接稳定的真正支点。

















