WebSocket连接状态检测必须依赖自身通信行为而非navigator.onLine;需结合onclose/onerror事件、readyState值、心跳双向验证(客户端发ping+服务端回pong+超时主动断连)、CloseEvent.code判断(如1006需重连)及指数退避重连机制。

WebSocket本身不感知网络层断开,navigator.onLine不能替代连接检测
浏览器的 navigator.onLine 只反映操作系统是否报告有网络接口(比如 Wi-Fi 开关开了、手机蜂窝已启用),但完全无法判断 WebSocket 连接是否真实可用。常见陷阱是:用户开着 Wi-Fi,但路由器断了上行、或防火墙拦截了 WebSocket 端口,此时 navigator.onLine === true,但 ws.readyState 早已卡在 1(OPEN)却收不到任何消息——连接实际已“假死”。
真正可靠的判断必须基于 WebSocket 自身状态和通信行为:
-
ws.onclose和ws.onerror是唯一能捕获连接异常的入口,但它们只在断开发生后触发,不具备预测性 -
ws.readyState值为0(CONNECTING)、1(OPEN)、2(CLOSING)、3(CLOSED)——但值为1不代表连接健康,只是“当时连上了” - 服务端主动关闭时会带
CloseEvent.code(如1000正常、1006异常、1013重试),这些 code 必须在onclose中显式读取,否则会被忽略
必须实现心跳机制,且心跳要双向验证
单纯客户端发 ping、服务端不回 pong,等于没做心跳。很多前端只在 setInterval 里调用 ws.send('ping'),却没监听服务端的响应,结果网络中断后仍认为连接正常。
正确做法是:客户端定时发 ping,同时启动一个超时计时器;服务端收到后必须立即回 pong;客户端收到 pong 后清除该计时器;若超时未收到,则判定连接失效并主动 ws.close()。
示例关键逻辑:
let heartbeatTimeout = null;
const HEARTBEAT_INTERVAL = 30000;
const HEARTBEAT_TIMEOUT = 10000;
function startHeartbeat() {
ws.addEventListener('message', (e) => {
if (e.data === 'pong') {
clearTimeout(heartbeatTimeout);
return;
}
});
const sendPing = () => {
if (ws.readyState === WebSocket.OPEN) {
ws.send('ping');
heartbeatTimeout = setTimeout(() => {
console.warn('Heartbeat timeout, closing connection');
ws.close(4999, 'heartbeat timeout');
}, HEARTBEAT_TIMEOUT);
}
};
setInterval(sendPing, HEARTBEAT_INTERVAL);
}
onclose 中必须检查 event.wasClean 和 event.code
不是所有 onclose 都是意外断开。event.wasClean === false 才表示非正常关闭(比如 TCP 断连、服务端崩溃、中间代理切断),这时才需要重连;而 wasClean === true 很可能是你或服务端主动调用了 close(),不应盲目重试。
event.code 更关键,它暴露了服务端意图:
-
1000:正常关闭,无需处理 -
1006:底层连接异常(最常见于断网),应立即重连 -
1012/1013:服务端要求重试,建议指数退避(如首次等 1s,下次 2s,再下次 4s…) - 其他自定义 code(如
4999):是你自己在心跳超时中主动 close 的,可按业务逻辑处理
漏掉这些判断,会导致重连风暴或该重连时不重连。
重连不能无脑 new WebSocket(),要控制频次与状态同步
每次 onclose 就立刻 new 一个新实例,极易触发浏览器并发连接限制(Chrome 默认约 6 个同域 WebSocket),尤其在弱网下形成雪崩。
必须加防抖和退避:
- 记录当前重连次数
reconnectAttempts,超过阈值(如 5 次)暂停自动重连,改为人手触发 - 使用
setTimeout实现指数退避,避免密集请求压垮服务端 - 重连成功后,需重新同步业务状态(比如重发未确认消息、恢复订阅列表),不能假设上次连接的上下文还存在
- 如果页面失焦(
document.hidden === true),应暂停心跳和重连,防止后台 Tab 耗电/占资源
最易被忽略的是:WebSocket 连接重建后,服务端并不知道你是“同一个用户”,除非你在握手阶段传 token 并在服务端做 session 绑定,否则在线状态可能重复计数或消息错乱。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











