闭包单例确保 WebSocket 全局唯一,核心是闭包内声明 instance 并在 getInstance 中首次创建、后续复用,同时手动管理连接状态、重连与销毁;模块热更新或 SSR 环境可能导致失效。

在单页应用(SPA)中,用闭包配合单例模式确保全局只有一个长连接通道(如 WebSocket),核心是控制初始化时机、封装连接状态、防止重复创建,并主动管理生命周期。不是写个 getInstance 就算单例,关键在于“同一引用 + 同一连接 + 同一状态”。
用立即执行函数封装私有实例与状态
把 WebSocket 实例、连接状态、重连计数器等全放在闭包内,外部无法直接访问或修改。模块加载时只执行一次,后续调用都复用这个作用域里的变量。
- 用
let instance = null声明,首次调用getInstance()才真正创建连接 - 避免在闭包顶层就执行
new WebSocket(),否则页面未就绪或环境不支持时会报错 - 连接对象本身(如
uni.connectSocket或原生WebSocket)必须赋值给闭包内变量,不能每次返回新对象
确保获取的是同一个对象引用
单例失效的典型表现是:两次 getInstance() 返回的对象 === 为 false。这说明你返回的是多个独立对象,哪怕它们共享闭包变量,也不符合单例定义。
- 正确做法:在
getInstance内部做判断并缓存,return instance || (instance = createConnection()) - 错误做法:每次调用都执行
createConnection()并返回新对象,即使闭包里有个counter被共用,实例本身仍是多个 - 验证方式:
console.log(manager1 === manager2)必须为true
手动补全连接生命周期管理逻辑
闭包单例不自动处理重连、断开清理、消息分发。这些必须显式实现,否则看似唯一,实则状态混乱。
- 监听
onClose和onError,触发指数退避重连,且重连时复用原实例,不新建 - 提供
destroy()方法清空instance并关闭底层连接,避免内存泄漏 - 若使用 uni-app,必须用
uni.connectSocket替代原生WebSocket,并在各端统一适配收发逻辑 - 消息回调不能直接绑定到
this,需确保上下文指向当前实例(避免箭头函数导致this指向错误)
注意模块加载与运行环境边界
ES 模块天然支持“一次加载、多次 import 共享同一闭包”,但某些场景下仍会打破单例契约:
- Webpack HMR 或 Vite 热更新时,模块可能被卸载重载,导致闭包重建、
instance重置 - 测试环境里多次
jest.mock或清理require.cache,也会让闭包重新执行 - SSR 中服务端渲染阶段不可用 DOM 或网络 API,需加判断延迟初始化,或区分客户端/服务端导出
- 不要依赖
globalThis或Symbol.for来跨模块共享——那已不属于闭包单例范畴,且增加键名冲突风险

















