闭包捕获引用是实现 WebSocket 单例代理最轻量可控的方式——通过 IIFE 封装、延迟初始化、url 常量化及状态守卫,确保唯一实例、懒加载与跨平台兼容。

闭包捕获的引用是实现 WebSocket 单例代理最轻量、最可控的方式——它不依赖类、不引入全局变量,靠作用域隔离确保实例唯一,同时天然支持懒加载与状态封装。
核心结构:IIFE 封装 + 延迟初始化
把 instance 声明在立即执行函数内部,仅暴露 getInstance 方法。首次调用才创建连接对象,后续直接返回同一引用:
- 用 let instance = null 声明,避免 var 提升导致污染
- getInstance(url) 内部做 !instance 判断,不是判断 readyState 或 socket 是否存在
- 连接逻辑(如 uni.connectSocket 或 new WebSocket)必须放在判断块内,不可提前执行
- 返回的对象需包含 connect、send、close、getReadyState 等统一接口,屏蔽底层差异
关键细节:防止 url 不一致引发多实例
闭包单例对 url 高度敏感——传入不同地址不会覆盖原实例,而是被忽略或误判为“需新建”,结果可能产生多个连接:
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- 所有调用必须传入完全相同的 url(协议、域名、端口、查询参数均需一致)
- 推荐将 url 提前定义为常量,例如 const WS_URL = 'wss://api.example.com/ws',各处 import 使用
- 若需区分环境(dev/staging/prod),应通过构建时变量或配置中心注入,而非运行时拼接
- 小程序中 url 必须为 wss://,H5 调试可用 ws://,但不能混用
状态与生命周期协同:避免组件重挂载触发重复连接
单例本身解决“只一个实例”,但组件反复挂载(如 keep-alive 页面切换)仍可能多次调用 getInstance,需配合状态守卫:
- connect 方法内必须检查当前 socket 状态:readyState === WebSocket.CONNECTING || WebSocket.OPEN 时直接返回
- onClose 回调中触发重连前,先清除已有定时器(clearTimeout(reconnectTimer)),再设新定时器
- send 方法需校验 readyState === WebSocket.OPEN,未就绪时可暂存消息队列,待 onOpen 后批量发送
- 不建议在组件 setup 或 onMounted 中主动调用 connect(),应由业务模块按需触发,或在应用启动时统一初始化
跨平台兼容要点(尤其 uni-app)
uni-app 的 WebSocket API 行为与标准浏览器差异明显,闭包代理必须适配:
- 必须使用 uni.connectSocket,不可用 new WebSocket;且需确保在 onReady 后调用,onLoad 阶段大概率失败
- onSocketOpen、onSocketMessage 等监听需在 connect 后立即注册,且不能重复绑定(uni.on* 是全局监听,需确保只 bind 一次)
- 查询参数必须 encodeURIComponent,例如 token=abc+def 要编码为 token=abc%2Bdef,否则连接中断
- 断开时调用 uni.closeSocket(),而非 this.socketTask?.close()










