前端通过 SSE / ReadableStream 接收大模型流式输出时,UI 渲染慢、内存容易暴涨,应该如何优化?
流式场景(如 AI 对话)的核心是「边读边处理,不 accumulate 全量」。
1. 用 ReadableStream 逐块消费,而不是等全部读完
const res = await fetch('/api/stream')
const reader = res.body.getReader()
const decoder = new TextDecoder()
while (true) {
const { done, value } = await reader.read()
if (done) break
const chunk = decoder.decode(value, { stream: true })
appendToUI(chunk) // 逐块追加,不保存全量字符串
}2. 避免内存暴涨的关键点
- 不要拼接整个响应:用
array.push(chunk)后立刻渲染,或干脆不缓存,只在 UI 上累加(必要时只保留最终结果)。 - 批量更新 UI:高频小块直接操作 DOM 会卡顿,用
requestAnimationFrame或节流合并多次更新(每帧只重绘一次)。 - SSE 用原生 EventSource / 或 fetch + ReadableStream,监听
onmessage逐条处理。 - 解码器复用:
TextDecoder复用同一个实例,避免每片 new。
3. 多 Session / 切换标签页的状态隔离
- 用「草稿(draft) / 生成中(generating)」状态机管理,每个会话独立保存流式内容。
- 切走标签页时 rAF 暂停,回来再续;用
visibilitychange控制是否继续追加渲染。
4. 内存泄漏排查
- 流式回调里引用的组件若已卸载,要取消 reader(
reader.cancel())并解绑监听。 - 长对话注意只保留必要上下文,旧消息可裁剪或摘要压缩。
一句话:流式渲染 = 增量消费 + 合并更新 + 及时释放,不要等「全部到齐」再处理。