onopen才是安全发送消息的唯一时机,onmessage需按evt.data类型分支处理,onerror不应调close(),onclose才是重连起点且须立即绑定事件监听。

onopen 不是“连接好了就能发”,onmessage 的 evt.data 不是 JSON 对象,onerror 里调 close() 是错的,onclose 才是重连的起点。
onopen 里才能安全调用 send()
很多人在 new WebSocket(url) 后立刻写 ws.send(),结果报 InvalidStateError。因为此时 ws.readyState 很可能还是 0(CONNECTING),TCP 握手甚至都没完成。
真正安全的位置只有 onopen 回调内部——它被触发时,ws.readyState 必定是 1(OPEN),且底层通道已就绪。
- 别在
onopen外部做任何发送逻辑,包括初始化消息、心跳启动 - 服务端如果用了 Node.js +
ws库,connection和open事件之间有微小延迟,onopen里发的第一条消息仍可能被丢,建议加个setTimeout(..., 0)或服务端确认机制 - 如果服务器返回 403/429 等非 101 响应码,
onopen根本不会触发,而是走onerror→onclose流程
onmessage 必须主动判断 evt.data 类型
evt.data 可能是 string、Blob 或 ArrayBuffer,取决于服务端发送方式和客户端 binaryType 设置。直接 JSON.parse(evt.data) 在二进制场景下会炸。
稳妥做法是按实例类型分支处理:
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
ws.onmessage = function(evt) {
let data;
if (evt.data instanceof Blob) {
data = await evt.data.text();
} else if (evt.data instanceof ArrayBuffer) {
data = new TextDecoder().decode(evt.data);
} else {
data = evt.data;
}
try {
const parsed = JSON.parse(data);
console.log(parsed);
} catch (e) {
console.warn("消息解析失败", data);
}
};
- 别依赖
typeof evt.data === "string"判断——某些浏览器对小 ArrayBuffer 也会返回 string - 高频消息场景(如每秒 50+ 条)下,
JSON.parse()会阻塞主线程,考虑用structuredClone()或 Web Worker 搬运 - 如果服务端固定发二进制(如 protobuf),提前设
ws.binaryType = "arraybuffer",避免 Blob 异步读取开销
onerror 不代表断连,更不该调 close()
onerror 是协议层异常信号(比如帧损坏、证书失败、升级被拒),但它不表示连接已关闭——ws.readyState 此时可能仍是 0 或 1,且后续几乎必然紧跟着 onclose。
- 在
onerror里调ws.close()会导致InvalidStateError,因为连接可能还没真正建立或已经半关闭 - 该做的事:记录
evt.target.url和当前ws.readyState,清除未决的心跳定时器,触发带防抖的重连(比如setTimeout(initWebSocket, 1000)) - 注意:有些网络中间件(如 Cloudflare)空闲 100 秒后单向断连,
readyState仍显示OPEN,但下次send()就直接抛错,必须靠心跳 +onerror联合捕获
onclose 是唯一可靠的重连入口
onclose 才标志连接终结,且携带 event.code 和 event.reason,能帮你区分是服务端主动关(如 4001)、客户端调 close()(1000)、还是网络中断(1006)。
- 重连逻辑必须放
onclose里,而不是onerror;否则 1006 这类异常会漏掉 - 不要在
onopen里写重连——它只触发一次,重连是onclose后new WebSocket()的事 -
event.code === 1000表示正常关闭,一般不用重连;1006(abnormal closure)或4000+(自定义错误码)才应触发重试
最易被忽略的是:事件监听必须在 new WebSocket() 后**立即绑定**,不能等异步操作完成。一旦漏绑,onopen 或 onerror 就永远丢失——浏览器不会缓存这些事件。










