onmessage未触发主因是连接未真正就绪或消息类型不匹配:需确保readyState===1后注册监听、确认服务端发送TextMessage而非二进制帧、避免监听器被重复赋值覆盖,并添加心跳防中间件静默断连。

onmessage 没触发,90% 不是代码写错了,而是连接没真正在收数据——readyState !== 1、消息类型不匹配、监听器被覆盖,这三类问题占全部现场的绝大多数。
检查 readyState 是否真的等于 WebSocket.OPEN(即 1)
WebSocket 构造函数返回实例后,readyState 初始值是 0(CONNECTING),不是立刻变成 1。此时绑定 ws.onmessage 就像往还没通电的插座上插电器。
- 永远在
ws.onopen回调里注册onmessage,而不是 new 之后马上赋值 - 如果必须提前定义 handler,至少加一层保护:
if (ws.readyState === WebSocket.OPEN) { handle(event); } - 在控制台手动执行
ws.readyState,确认它确实是1;如果是0或2(CLOSING)或3(CLOSED),说明连接根本没活过来 - Network 面板过滤
ws,看连接状态是否为 “Active”,有没有 400/401 握手失败记录
确认服务端发的是 TextMessage 还是二进制帧
onmessage 收到的 event.data 类型由服务端发送时的数据类型决定,不是固定字符串。强行 JSON.parse(event.data) 报错,大概率是因为你收到的是 ArrayBuffer。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- 在
onmessage开头加一句:console.log(typeof event.data, event.data),一眼看清是string、ArrayBuffer还是Blob - 服务端用
send("...")(字符串)→ 前端event.data是 string → 可直接JSON.parse() - 服务端用
send(new Uint8Array([...]))→ 前端event.data是 ArrayBuffer → 需用new TextDecoder().decode(event.data)转成字符串 - Java 后端用
@OnMessage注解默认只接收TextMessage;若前端发二进制,后端完全不进该方法,日志静默——去 Network → WS → Frames 看“Type”列是 Text 还是 Binary
排查 onmessage 被重复赋值覆盖
在 SPA 页面反复挂载/卸载、uniapp 的 onShow/onHide、React 的 useEffect 中无条件执行 ws.onmessage = handler,会导致只有最后一次赋值生效,前面的逻辑全丢。
- 改用
ws.addEventListener('message', handler),并保存 handler 引用,便于后续removeEventListener - 如果坚持用赋值方式,每次新建连接前先清理旧引用:
oldWs?.onmessage = null - 注意:WebSocket 实例不可复用,
close()后再new一个,旧实例上的所有事件监听器自动失效(截至 2026 年 5 月) - uniapp 中尤其容易漏掉:页面
onUnload时未socketTask.close(),下次进入又建新实例,老监听器还挂着但已无法触发
别忽略中间件空闲断连和心跳缺失
连接显示 OPEN,但 onmessage 偶尔失灵或彻底停摆?很可能是 Nginx、Spring Cloud Gateway 或云 WAF 在后台悄悄关掉了“静默连接”。
- 默认空闲超时多为 60 秒,只要 60 秒内没帧收发,中间件就发 FIN 关闭 TCP 连接,但前端
ws.readyState仍显示1,后续 send 或 onmessage 全部失效 - 在
onopen后立即启动定时器:setInterval(() => ws.send('ping'), 30000) - 服务端必须响应(哪怕只是 echo),否则心跳无效;Spring 项目需检查
ReactorNettyWebSocketService的maxIdleTime配置 - 小程序环境更敏感:
onNetworkStatusChange触发时,必须主动close()再重连,系统不会恢复底层 socket 句柄
真正难排查的点往往藏在「连接看似正常」的假象里:readyState 是 1、onopen 触发了、Network 显示 Active,但 Frames 标签页里压根没有收到任何帧——这时候要倒回去看服务端发的是什么类型、中间件有没有静默断连、监听器是不是早被覆盖了三次。

















