WebSocket卡顿根源是主线程JSON序列化阻塞渲染,必须用Web Worker零拷贝处理、二进制帧+紧凑编码、服务端推delta及三层过滤,并分层解包。

主线程里直接 JSON.stringify() 或 JSON.parse() 大 JSON,是 WebSocket 卡顿的根源——不是“慢一点”,而是阻塞渲染、掉帧、用户操作无响应。解决它不靠压缩技巧,而靠把序列化任务从主线程彻底移出去。
必须用 Web Worker 做序列化和反序列化
10MB 状态对象在主线程 stringify 一次,轻松卡住 UI 100ms+。这不是优化问题,是架构错误。
- 主页面只传原始对象或 ArrayBuffer 给 Worker,用
postMessage(data, [transferList])零拷贝传递 - Worker 内用
structuredClone()深拷贝含 Date/Map/Set 的数据,再调用JSON.stringify()或更优方式(如TextEncoder.encode(JSON.stringify(...))) - 接收端同理:Worker 收到二进制帧后用
TextDecoder.decode()+JSON.parse(),结果再 postMessage 回主线程 - Worker 里加耗时监控:
performance.now()超过 20ms 就告警并降级为简化格式
改用二进制帧 + 紧凑编码,绕开 JSON 文本开销
字段名重复、数字转字符串、引号空格冗余,让 JSON 体积比二进制大 2–4 倍,解析也慢 3–5 倍。
- 前端设置
ws.binaryType = "arraybuffer",服务端发Opcode=0x2二进制帧 - 高频字段(如
"type": "update")用享元编码:预定义类型 ID 表,只传Uint16Array[2, 17](2 是 type 字段 ID,17 是值 ID) - 结构稳定时上 MessagePack 或 protobufjs,JS 库直出
Uint8Array,零字符串中间态 - 避免在循环中反复 new 序列化器,复用实例或用无状态函数
服务端配合:只推 delta,不广播全量
前端再快,也扛不住服务端每秒给一万个客户端发 200KB 全量 JSON。瓶颈常在广播路径本身。
立即学习“Java免费学习笔记(深入)”;
- 服务端维护状态快照(Redis Hash / Caffeine),新连接拉一次全量;后续只推
{"score_delta": +120, "rank": 3}这类差分消息 - 启用 burst merge:100ms 窗口内 37 条“点赞”打包成数组下发,减少帧数和序列化调用次数
- 广播前做三层过滤——按房间 ID、在线状态、权限等级跳过无效连接,省下大量无意义序列化
- 客户端收到 delta 后,在 Worker 里合并到本地快照,主线程只收最终状态更新
解包阶段也要分层解耦
别让“字节→对象→业务逻辑”挤在同一个函数里。解包必须可监控、可裁剪、可降级。
- 第一阶段纯数据转换:Worker 把 ArrayBuffer 解包成标准化 DTO(如
{type: 'chat', id: 123, content: 'hi'}),不碰任何业务逻辑 - 第二阶段批量消费:主线程用调度器统一处理一批 DTO,支持合并渲染、异步落库、限流熔断
- 按连接上下文动态裁剪:游客连接收到群聊消息,只解
sender和content,跳过read_receipts等扩展字段 - 用线上采样数据统计字段出现频次,针对性优化解析路径,而不是追求“通用解包器”



















