为防止页面切后台导致心跳失效,应结合Page Visibility API动态启停心跳、基于lastPongTime按需补发ping、启用TCP keepalive辅助探测,并在小程序中监听App.onHide/onShow主动关闭并重建连接。

页面切到后台时,浏览器会节流甚至暂停 setInterval 和 setTimeout,导致心跳定时器失效,连接在几十秒内被代理或防火墙静默断开。要维持心跳,关键不是“强行发包”,而是适配浏览器生命周期,用更可靠的方式感知活跃状态并按需驱动心跳。
监听页面可见性变化,动态启停心跳
利用 document.visibilityState 和 visibilitychange 事件,在页面不可见时暂停发送,回到前台立即恢复并补发一次心跳,避免空窗期过长:
- 监听
document.addEventListener('visibilitychange', ...),当document.hidden === true时调用clearInterval(heartbeatInterval)和clearTimeout(pongTimeout) - 切回前台(
visibilityState === 'visible')时:检查连接状态是否仍为OPEN,是则立即发一次"ping"并重置响应等待定时器 - 避免在隐藏状态下堆积未发送的心跳——不发比乱发更安全
用 Page Visibility API 替代纯时间驱动
不依赖固定间隔轮询,改用「最后一次收到 pong 的时间 + 最大空闲阈值」判断是否需补救:
- 记录
lastPongTime = Date.now(),每次收到服务端"pong"时更新 - 在
visibilitychange触发时,计算Date.now() - lastPongTime - 若差值接近或超过服务端空闲超时(如 50 秒),立即尝试发一次 ping 并设短超时(如 5 秒),快速验证连接是否还活着
补充 TCP 层保活(可选但推荐)
虽不能替代应用层心跳,但能辅助检测底层链路异常:
立即学习“Java免费学习笔记(深入)”;
- 服务端开启
TCP keepalive(如 Node.js 中ws.socket.setKeepAlive(true, 60000)),每 60 秒发一次探测包 - 该机制对 NAT 超时有一定缓解作用,但注意:它只反映 TCP 可达性,无法发现服务端卡死、页面冻结等业务层失效
- 必须与应用层心跳配合使用,不可单独依赖
小程序等特殊环境需额外处理
微信小程序中 wx.connectSocket 在页面退后台后约 30 秒会被系统强制关闭,此时仅靠 visibility 监听不够:
- 必须监听
App.onHide()主动调用wx.closeSocket(),并在onShow()中重建连接 - 重连后立即同步状态(如重新订阅行情频道、恢复 lastMsgId),而非等待下一次心跳触发
- 服务端需支持连接复用标识(如带 token 的 URL),便于识别同一用户的新旧连接,平滑迁移上下文


















