单页应用中应封装全局WebSocket模块实现单连接、跨组件共享、自动重连、状态可控和消息订阅;采用模块单例+闭包隔离状态,导出connect/send/on/close接口,内置readyState检查与type消息订阅,解耦路由,处理认证、重试、队列及Token刷新,并兼顾跨端兼容性。

用模块单例 + 闭包隔离状态
把 WebSocket 实例、重连定时器、消息队列、认证状态等全部锁在模块作用域内,导出统一接口:
- ESM 模块天然支持单例:所有 import 都指向同一份初始化逻辑,避免多次 new
- 不把 socket 暴露给外部,只提供 connect()、send()、on(type, handler)、close() 四个受控方法
- readyState 检查内置在 send/connect 中,调用方无需手动判断
- 消息按 type 订阅,支持多个 handler 同时注册,不互相覆盖
连接生命周期与路由解耦
SPA 页面跳转 ≠ 应用退出。不能让某个页面的卸载触发 close(),否则一刷新或切页就断连:
- 连接由根组件(如 App.vue 或 _app.ts)或主入口统一启动,与 Vue Router / React Router 无关
- 各页面只调用 on('order', cb) 或 on('notify', cb) 订阅关心的消息类型
- 需要业务隔离时,靠消息体里的
type字段过滤,而不是开多个连接 - 小程序 onShow、网页 visibilitychange 时可触发轻量心跳或状态校验,不强制重连
必须处理的四个关键状态细节
readyState === 1 不代表“可用”,真实可用需结合业务语义:
- 连接但未认证:send auth 消息后设 pendingAuth = true,收到服务端 success 响应才标记为“就绪”
- 断连自动重试:onclose 触发指数退避重连(1s → 2s → 4s…),上限建议 10 次,避免打爆后端
- 发送兜底队列:send() 时若未就绪(如 connecting/closed),暂存消息数组,onopen 后批量 flush
- Token 过期主动刷新:收到 auth_expired 响应时,先调登录接口换 token,再 close + reconnect,不依赖定时轮询
跨端兼容性硬约束(尤其 uni-app)
不同平台 WebSocket 行为差异大,必须前置校验:
立即学习“Java免费学习笔记(深入)”;
- URL 必须用 wss://(小程序强制;H5 开发阶段可临时 ws://,但上线前必须切换)
- query 参数完整 encodeURIComponent,例如
token=abc+def→token=abc%2Bdef - uni-app 中
uni.connectSocket必须在onReady后调用,onLoad阶段调用必失败 - 原生 WebSocket 构造函数在低版本浏览器可能不存在,需做
typeof WebSocket !== 'undefined'检查


















