必须在 onready 后调用 uni.connectsocket 且使用 wss:// 协议,加 isconnected 标志防重复,token 需 encodeuricomponent,发消息前须等 uni.onsocketopen,data 要 json.stringify,断线需指数退避重连并区分错误码。

直接用 uni.connectSocket 就能连上,但不封装就等于裸奔——断网不重试、切后台失联、多页面抢连接、发消息静默失败,上线后用户一断网你就收不到告警。
为什么不能在 onLoad 里调用 uni.connectSocket
App 和小程序的 WebView 或原生容器在 onLoad 阶段还没完全就绪,此时调用 uni.connectSocket 容易静默失败,或触发延迟超时。H5 看似宽松,但为跨端一致,所有平台都应等 onReady 触发后再初始化。
-
onReady是页面真正可交互、底层网络栈可用的最稳妥时机 - 若项目用了分包懒加载或 Vue Router,还要防重复触发:加个
isConnected标志位或单例锁 - 别依赖
mounted(Vue2)或onMounted(Vue3),它们在 App 端不等价于页面就绪
uni.connectSocket 的 URL 必须是 wss:// 且参数要 encodeURIComponent
小程序平台(微信/支付宝/抖音)强制要求加密协议,ws:// 直接报错;H5 和 App 虽支持 ws://,但生产环境的 CDN、Nginx 或网关通常只放行 wss://,所以一律用 wss://。
- 带 Token 的 URL 示例:
wss://api.example.com?token=abc+def→ 必须编码为wss://api.example.com?token=abc%2Bdef - 未编码的
+、空格、/会导致握手失败,错误信息通常是fail err: {"errno":100001,"errMsg":"connectSocket:fail"} - 小程序不支持自定义 header(除
content-type外),Token 只能走 query 参数,别往 header 里塞
发消息前必须等 uni.onSocketOpen,且 data 只能是字符串或 ArrayBuffer
uni.connectSocket 的 success 回调只表示“连接已发起”,不代表通道就绪;uni.onSocketOpen 才代表 WebSocket 协议握手完成,此时才能安全收发。
- 立刻在
success里调uni.sendSocketMessage({ data }),大概率报fail websocket not connected -
data字段只接受string或ArrayBuffer,传Object会静默失败,必须手动JSON.stringify() -
uni.onSocketMessage的event.data类型由服务端决定:文本 →string,二进制 →ArrayBuffer,需先typeof event.data === 'string'判断再解析
断线重连必须手动实现,且要区分平台行为和错误码
uni-app 不提供自动重连。uni.onSocketClose 在微信小程序里常不触发(切后台静默断连),不能只靠它判断失效。必须主动心跳 + 指数退避重连。
- 监听三个关键事件:
uni.onSocketError(DNS 失败、证书问题)、uni.onSocketClose(检查event.code)、onHide(App/小程序切后台时主动uni.closeSocket()) - 小程序切后台的
event.code是1001,属正常行为,别打错误日志;非1000/1001的 code 才需重连 - 重连别用固定间隔
setTimeout,改用指数退避:第 1 次 1s,第 2 次 3s,第 3 次 9s……最多试 5 次,避免打爆服务端 - 每次重连前调
uni.getNetworkType,无网络时不尝试,防止无效请求
真正的难点不在“连上”,而在“一直连着”——心跳定时器没清除干净会叠加,重连逻辑没过滤错误码会无限循环,多页面没共享单例会导致服务端连接数暴增。这些细节不压平,长连接就只是个纸面功能。











