流式更新要合并字段而非替换对象
一、Bug 形态
// 新增字段
interface ChatMessage { role; content; thinking?: string }
// 出错的地方:整体赋值,不是合并
updated[updated.length - 1] = { role: "assistant", content: assistantContent };
时序上只坏一瞬间:
t=0s 思考事件涌入(617 个)→ thinking 累积到 1742 字 ✅
t=10s 第一个 text 事件到达 → thinking 被整个清空 ❌ 就这一瞬间
t=10s+ 后续 text 事件 → 只更新 content
二、为什么特别隐蔽
- 时间窗口极窄:只在「思考流结束、正文第一个分片到达」的那一帧触发。
- 只在有思考的请求里出现:上游触发思考是概率性的(实测 4 次里 3 次有),无思考的请求完全正常,看起来像是"偶发"。
- 传输层是无辜的:用页面内
fetch直接抓流验证,后端确实发了 N 个 thinking 事件、前端也确实显示过。问题不在网络、不在上游,而在本地 state 更新方式。
三、通用规则
给已有对象新增可选字段时,任何「整体赋值」的更新点都是定时炸弹。 流式 / 增量更新一律 spread 合并({ ...prev, [field]: value }),或者用不可变更新工具。这类 bug 不会报错,只会静默丢字段。
相关
- 进度条要由真实进度驱动(同一天的前端状态与动画问题)
- 讲bug先讲现象再讲根因