SSE需解决消息乱码、高频卡顿、长文本阻塞三瓶颈:用ReadableStream+TextDecoder流式解码并缓冲完整消息;节流批量更新状态防响应式过载;分片+微任务+textContent渐进渲染长文本;组件卸载时关闭EventSource并清理任务队列。

SSE(Server-Sent Events)本身是单向流式通信机制,不自带响应式能力;它要真正“配合” Vue 或 React 实现响应式更新,关键不是把数据塞进状态变量就完事,而是解决三个实际瓶颈:消息乱码、高频更新卡顿、长文本阻塞主线程。直接用 useState 或 ref.value 接收每条 data: 消息,很容易导致 UI 掉帧或中文显示异常。
处理多字节字符与协议分隔符
SSE 协议以 \n\n 分隔消息,服务端若拼接不规范(比如中间插入换行或未补全 data: 前缀),客户端会收到截断片段。浏览器原生 EventSource 不处理 UTF-8 多字节边界,直接调用 TextDecoder.decode() 会把一个汉字或 emoji 拆成两段,解码失败变 。
- 正确做法:用
ReadableStream+TextDecoder流式解码,启用{ stream: true }(注意:该参数合法,部分文档误标为非法) - 手动缓冲未完成消息:维护一个
buffer字符串,每次收到event.data后追加,再按\n\n切分,只处理完整块 - Vue 中可封装为
useSSEStream(url)组合式函数,内部自动做缓冲与解码
节流批量更新避免响应式过载
服务端每秒推送 20 条 token,React 的 setState 或 Vue 的 ref.value = ... 若逐条触发,会引发上百次虚拟 DOM diff 和重排,输入框光标卡顿、滚动掉帧。
重要:对 React 或 Next.js 代码的任何更改必须先阅读本技能。Vercel 工程团队的 React 与 Next.js 指南,涵盖可视化...
- 默认 30ms 窗口内合并变更:收集所有新文本片段,一次性
setState(prev => prev + newChunk) - Vue 3 推荐用
shallowRef存原始字符串,配合nextTick批量赋值,避免深层响应式开销 - 不要在
onmessage回调里直接改ref或state,先入队列,再由节流器统一提交
时间切片渲染长文本防阻塞
一条 SSE 消息含 5000 字,直接 innerHTML = text 或 v-html 会占用主线程 >100ms,用户点击无响应。
立即学习“Java免费学习笔记(深入)”;
- 将长文本按 200 字左右切片,用
MessageChannel创建微任务队列,每片执行不超过 8ms - React 可结合
useTransition标记为非紧急更新;Vue 3 则用useTimeSlicedQueue自定义 hook 分批写入ref - 渲染时用
textContent替代v-html(除非需富文本),减少 HTML 解析开销
清理与错误恢复必须显式处理
页面跳转或组件卸载后,EventSource 若未关闭,会持续接收消息并尝试更新已销毁的组件实例,引发内存泄漏和 React warning “Can’t perform a React state update on an unmounted component”。
- React 中在
useEffect返回清理函数:es.close(),同时取消所有待执行的节流/切片任务 - Vue 中
onBeforeUnmount调用esRef.current?.close(),并清空useTimeSlicedQueue的任务队列 - 监听
onerror,检测连接中断后自动重试(带退避策略),避免静默失败

















