onclose不可靠因平台差异:微信小程序静默断连不触发、h5休眠延迟或丢失、app后台kill无通知;须用settimeout心跳+时间戳比对判定超时,配合指数退避重连与连接复用。

不能只靠 onClose 触发重连——微信小程序静默断连、H5 页面休眠时该回调根本不会执行,必须主动探测连接状态。
为什么 onClose 不可靠?
不同平台对 WebSocket 生命周期的处理差异极大:
- 微信小程序:切后台或页面隐藏后,连接被系统静默回收,
onClose完全不触发 - H5:标签页非激活时,浏览器可能冻结定时器,
onClose延迟数秒甚至丢失 - App(iOS/Android):后台保活权限未开启时,系统强制 kill socketTask,无任何通知
结论:仅监听 onClose 等同于放弃弱网和跨端场景下的连接恢复能力。
心跳检测必须用 setTimeout 而不是 setInterval
重连过程中若不清除旧定时器,setInterval 会不断叠加,导致心跳包雪崩式发送,服务端压力陡增甚至拒绝响应。
- 每次发送心跳前先
clearTimeout(this.heartbeatTimer) - 收到
pong后重置计时器,超时未响应则标记为HEARTBEAT_FAILED - 推荐心跳间隔
30000ms,服务端ping_timeout设为45000ms,留出网络抖动缓冲
重连策略要区分错误类型和次数
不是所有断开都该重试——比如服务端返回 401 认证失败,重连十次也没用;而网络抖动导致的闪断,则需快速恢复。
- 配置白名单错误码:
[1000, 1001, 4001](正常关闭、服务重启、业务拒绝)不重连 - 前 5 次重连固定间隔
3000ms;第 6 次起启用指数退避:1000 → 2000 → 4000 → 8000ms - 超过最大重试次数后,停止自动重连,抛出
HEARTBEAT_FAILED_MAX_RETRY事件供业务层决定是否手动触发
多页面共用一个 WebSocket 实例时,必须做连接复用与生命周期解耦
页面 A 打开 WebSocket,跳转到页面 B 后再返回,若不复用实例,会创建第二个连接,造成资源泄漏和消息重复接收。
- 用 Pinia store 缓存
socketTask实例,key 为 URL + token 组合 - 页面
onUnload或onHide时,仅移除消息回调函数,不调用close - 微信小程序需额外监听
onHide主动socketTask.close(),否则后台残留连接会阻塞后续新建
真正容易被忽略的是:心跳超时判定必须依赖时间戳比对,而不是单纯看 pong 是否到达——因为服务端可能延迟回复,但连接仍有效;反之,pong 到了但时间戳早于上次心跳发起时刻,说明网络严重乱序,也应视为异常。











