WebSocket连接需严格校验协议(ws://或wss://),绑定onopen/onmessage/onerror/onclose四事件,手动重连须带退避策略,发送前检查readyState=1,页面卸载前主动close。

WebSocket 连接建立前必须检查协议与地址格式
WebSocket 不是 HTTP,ws:// 和 wss:// 是唯一合法协议前缀,用 http:// 或相对路径(如 /ws)会直接报错 Invalid URL。浏览器控制台里看到 “Failed to construct ‘WebSocket’” 多半是这里出问题。
常见错误写法:
-
new WebSocket('localhost:8080')❌(缺协议) -
new WebSocket('/api/ws')❌(HTTP 路径,非 WebSocket 地址) -
new WebSocket('https://example.com/ws')❌(HTTPS 不能直连 WS,需 wss)
正确写法示例:
- 开发环境:
new WebSocket('ws://localhost:8080') - 生产环境:
new WebSocket('wss://api.example.com')(后端需配置 TLS + WebSocket 支持) - 动态适配:
const wsUrl = location.protocol === 'https:' ? 'wss://' : 'ws://' + location.host
onopen/onmessage/onerror/onclose 四个事件必须全部绑定且及时
只监听 onmessage 是高危操作——连接失败、断开、异常时无任何反馈,前端“静默失联”,用户以为功能正常,实际已卡死。
建议在实例化后立即绑定,避免竞态:
-
onopen:标记连接就绪,可启动心跳或重发队列;不要在此处重复 send,因可能尚未 ready -
onmessage:用event.data接收,注意类型——文本自动为 string,二进制默认为Blob,需设ws.binaryType = 'arraybuffer'才得 ArrayBuffer -
onerror:仅表示连接层异常(如 DNS 失败、被拦截),不触发 onclose;此处适合打日志,不要关闭 ws 实例(它可能正在重连) -
onclose:携带event.code和event.reason,区分是服务端主动关(如 1000)、网络中断(1006)、还是协议错误(1002);这是触发重连逻辑的唯一可靠时机
手动实现重连必须加退避策略和最大尝试限制
WebSocket 没有内置重连。裸写 ws.onclose = () => new WebSocket(...) 极易引发雪崩——网络抖动时瞬间新建数百连接,压垮客户端和服务端。
一个轻量但健壮的重连模板:
- 用闭包保存重连次数
let retryCount = 0 - 每次重连前:
retryCount++,若 ≥ 5 次则停止并提示用户 - 延迟时间取
Math.min(1000 * Math.pow(2, retryCount), 30000)(即 1s → 2s → 4s → 8s → 16s,上限 30s) - 使用
setTimeout而非setInterval,避免多次触发叠加 - 在
onopen中重置retryCount = 0
发送消息前务必校验 readyState 并合理处理缓冲
ws.readyState 有四个值:0=CONNECTING、1=OPEN、2=CLOSING、3=CLOSED。只有 1 时才能安全 send。
常见陷阱:
- 在
onopen外部立刻 send → 此时可能仍为 CONNECTING(尤其弱网),抛InvalidStateError - 连接断开期间积压消息,重连后未重发 → 功能断层
解决方案:
- 封装
send(data)方法:先判断if (ws.readyState === 1) ws.send(data),否则存入队列 - 在
onopen中清空并逐条发送队列(注意节流,避免 burst) - 对关键消息(如登录凭证、指令)加唯一 ID 和超时重试,服务端配合幂等设计
关闭连接要主动调用 close(),别依赖页面卸载
用户刷新/关闭标签页时,浏览器会自动终止 WebSocket,但这个过程不可控、不及时,服务端可能长时间保留无效连接(占用 fd、内存、心跳资源)。
正确做法:
- 在
beforeunload或pagehide事件中调用ws.close(1000, 'user leave') - 避免在
onclose里再调一次close()(防止重复关闭报错) - 服务端收到 code 1000 应立即清理会话,而非等待心跳超时
额外提醒:Vue/React 组件卸载、React Router 路由切换时,务必在 useEffect cleanup 或 componentWillUnmount 中关闭 ws,防止内存泄漏和重复监听。


















