必须在 connectSocket 成功且 onSocketOpen 触发后注册监听,推荐用 socketTask.onMessage() 并在页面卸载时清理;注意 res.data 类型判断与校验,避免重复注册和协议/域名配置错误。

uni.onSocketMessage() 必须在 connectSocket 之后调用
很多人写完 uni.connectSocket() 就直接监听消息,结果收不到任何数据——因为 uni.onSocketMessage() 是全局监听器,但它的回调只对「已建立连接后收到的消息」生效。如果在连接打开前就注册,或注册后连接未成功,消息会静默丢失。
正确顺序是:先确保 uni.onSocketOpen() 触发,再调用 uni.onSocketMessage();更稳妥的做法是把监听逻辑放在 onOpen 回调里,或统一在连接任务实例上绑定:
-
socketTask.onMessage()(推荐):用uni.connectSocket()返回的 task 实例绑定,生命周期更可控 - 避免混用
uni.onSocketMessage()和socketTask.onMessage(),否则可能重复触发或覆盖 - 若使用 Vue 3 +
setup,注意监听器注册时机不能早于onMounted,否则组件还没挂载,监听无效
收到的 res.data 不一定是 JSON 字符串
WebSocket 传输的数据类型由服务端决定,uni.onSocketMessage() 的 res.data 类型可能是 string、ArrayBuffer 或 Blob。UniApp 在小程序平台默认返回 string,但在 App 平台(尤其是 iOS)可能返回 ArrayBuffer,直接 JSON.parse(res.data) 会报错 Unexpected token in JSON at position 0。
- 先判断类型:
typeof res.data === 'string'再解析,否则用new TextDecoder().decode(res.data)转字符串 - 服务端若固定发 JSON,建议加个简易校验:
res.data.trim().startsWith('{') || res.data.trim().startsWith('[') - 不要在
onMessage里做耗时解析(如大对象 deep clone),否则阻塞主线程,导致后续消息堆积
消息监听器被重复注册导致多次执行
这是最隐蔽也最常见的 bug:页面反复进入(比如 tab 切换、路由 back/forward)、组件销毁后未清理监听器,uni.onSocketMessage() 会被反复调用,每次注册一个新监听,最终一条消息触发 N 次处理逻辑。
- 全局监听器无法手动移除,所以禁止在页面级反复调用
uni.onSocketMessage() - 改用 task 实例监听:
socketTask.onMessage(callback),并在页面onUnload或onBeforeUnmount时设为null(UniApp 不提供offMessage,只能替换回调) - 更健壮的做法是封装单例 Socket 管理器,在应用启动时初始化一次,所有页面共用同一个
socketTask和监听器
小程序真机调试收不到消息?检查 wss 协议和域名白名单
微信小程序要求 WebSocket 必须使用 wss://(不是 ws://),且域名必须提前配置在「小程序后台 → 开发管理 → 业务域名」中,否则真机下 onOpen 都不会触发,更别说 onMessage。
- H5 和 App 端可直连
ws://,但小程序强制加密,本地调试用wss://localhost会失败,需用合法证书域名或走代理 - 支付宝/百度小程序也有类似限制,但错误提示更模糊,常表现为「连接成功但无消息」,优先查控制台 Network 标签页的 WebSocket 连接状态
- 真机日志里如果看到
net::ERR_CONNECTION_REFUSED或Failed to execute 'send' on 'WebSocket': Still in CONNECTING state,基本就是协议或域名问题
onMessage 触发时,socketTask.readyState === 1,且当前监听器是唯一有效的那个。


















