如何利用“单例模式”配合闭包确保在单页应用中全局仅存在一个 WebSocket 长连接实例

浅浩君_7960

浅浩君_7960

2026-04-13

193人浏览

原创

不能直接多次 new websocket(),因会导致多连接并存、资源浪费、消息重复、重连混乱,且跨端不兼容;闭包单例通过 iife 锁定 instance 实现唯一初始化,但需配合 uni.connectsocket、状态清理、指数退避重连等手动补全逻辑。

如何利用“单例模式”配合闭包确保在单页应用中全局仅存在一个 websocket 长连接实例

为什么不能直接 new WebSocket() 多次调用

在单页应用里,每个页面或组件都 new WebSocket(url),会导致多个物理连接并存。服务端看到的是多个独立 client ID,资源占用翻倍,消息广播可能重复推送给同一用户,重连逻辑也各自为政。更糟的是,uni-app、Taro 等跨端框架中,new WebSocket() 在小程序和 App 端根本不可用——它只在 H5 有效,跨端一致性直接崩塌。

闭包单例如何避免多次初始化

核心是把 instance 变量锁在 IIFE 内部,外部无法篡改,且仅首次调用 getInstance() 时才真正创建连接:

const WebSocketSingleton = (function () {
  let instance = null
  return {
    getInstance: function (url) {
      if (!instance) {
        // 这里必须用平台兼容的 API,比如 uni-app 用 uni.connectSocket
        instance = {
          url,
          socket: null,
          connect() {
            uni.connectSocket({ url })
            uni.onSocketOpen(() => { /* 存引用 */ })
            uni.onSocketMessage((e) => { /* 转发给监听者 */ })
          },
          send(data) { uni.sendSocketMessage({ data }) }
        }
      }
      return instance
    }
  }
})()
  • 第二次调用 WebSocketSingleton.getInstance('wss://...') 不会新建连接,而是返回上次的 instance
  • 即使传入不同 url,也不会覆盖——这反而是个坑:你得确保所有地方传的 url 一致,否则闭包里会误判为“新实例”而新建连接
  • 如果需要支持多 endpoint(如不同环境),建议把 url 提到配置层,不在 getInstance 参数里传

uni-app 下必须用 uni.connectSocket,不是可选项

uni-app 的 uni.connectSocket 是跨端统一入口,但行为差异大:

WebSocket 8.18.2
WebSocket 8.18.2

WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。

下载
  • uni.connectSocket 必须在 onReady 后调用,onLoad 阶段调用在 App 端大概率失败
  • URL 协议必须是 wss://,小程序强制校验,ws:// 只能用于 H5 本地调试
  • 查询参数要 encodeURIComponent,例如 ?token=abc+def 不编码会导致连接中断
  • 发送消息前必须等 uni.onSocketOpen 触发,否则报错 fail websocket not connected

断开重连和状态清理最容易被忽略

单例不等于永生。切后台、网络切换、小程序被回收都会触发 uni.onSocketClose,但此时 instance.socket 仍非 null,下次 send 就静默失败。

  • 每次 uni.onSocketClose 或 uni.onSocketError 后,必须清空 instance.socket = null
  • 重连不能用固定间隔 setTimeout,得用指数退避:第 1 次 1s,第 2 次 3s,第 3 次 9s……最多试 5 次
  • App 和小程序的 onHide 生命周期里,应主动 uni.closeSocket() 并重置状态,否则切回来时连接已失效却还挂着“已连接”假象

闭包单例管住的是“实例唯一性”,但连接的活性、错误恢复、生命周期同步,全得靠手动补全——这些逻辑一旦漏掉,单例就只是个看起来整洁的空壳。

相关专题

更多
C语言变量命名
C语言变量命名

c语言变量名规则是:1、变量名以英文字母开头;2、变量名中的字母是区分大小写的;3、变量名不能是关键字;4、变量名中不能包含空格、标点符号和类型说明符。php中文网还提供c语言变量的相关下载、相关课程等内容,供大家免费下载使用。

2023.06.20

3229

3

c语言入门自学零基础
c语言入门自学零基础

C语言是当代人学习及生活中的必备基础知识,应用十分广泛,本专题为大家c语言入门自学零基础的相关文章,以及相关课程,感兴趣的朋友千万不要错过了。

2023.07.25

2328

9

c语言运算符的优先级顺序
c语言运算符的优先级顺序

c语言运算符的优先级顺序是括号运算符 > 一元运算符 > 算术运算符 > 移位运算符 > 关系运算符 > 位运算符 > 逻辑运算符 > 赋值运算符 > 逗号运算符。本专题为大家提供c语言运算符相关的各种文章、以及下载和课程。

2023.08.02

1260

5

c语言数据结构
c语言数据结构

数据结构是指将数据按照一定的方式组织和存储的方法。它是计算机科学中的重要概念,用来描述和解决实际问题中的数据组织和处理问题。数据结构可以分为线性结构和非线性结构。线性结构包括数组、链表、堆栈和队列等,而非线性结构包括树和图等。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.09

1218

4

c语言random函数用法
c语言random函数用法

c语言random函数用法:1、random.random,随机生成(0,1)之间的浮点数;2、random.randint,随机生成在范围之内的整数,两个参数分别表示上限和下限;3、random.randrange,在指定范围内,按指定基数递增的集合中获得一个随机数;4、random.choice,从序列中随机抽选一个数;5、random.shuffle,随机排序。

2023.09.05

1356

5

c语言const用法
c语言const用法

const是关键字,可以用于声明常量、函数参数中的const修饰符、const修饰函数返回值、const修饰指针。详细介绍:1、声明常量,const关键字可用于声明常量,常量的值在程序运行期间不可修改,常量可以是基本数据类型,如整数、浮点数、字符等,也可是自定义的数据类型;2、函数参数中的const修饰符,const关键字可用于函数的参数中,表示该参数在函数内部不可修改等等。

2023.09.20

2198

7

c语言get函数的用法
c语言get函数的用法

get函数是一个用于从输入流中获取字符的函数。可以从键盘、文件或其他输入设备中读取字符,并将其存储在指定的变量中。本文介绍了get函数的用法以及一些相关的注意事项。希望这篇文章能够帮助你更好地理解和使用get函数 。

2023.09.20

3520

8

c数组初始化的方法
c数组初始化的方法

c语言数组初始化的方法有直接赋值法、不完全初始化法、省略数组长度法和二维数组初始化法。详细介绍:1、直接赋值法,这种方法可以直接将数组的值进行初始化;2、不完全初始化法,。这种方法可以在一定程度上节省内存空间;3、省略数组长度法,这种方法可以让编译器自动计算数组的长度;4、二维数组初始化法等等。

2023.09.22

16175

6

c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

2023.09.22

549

3

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
WebSocket手册
WebSocket手册

共0课时 | 0人学习

Webman中文手册
Webman中文手册

共0课时 | 0人学习