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