Vue3 接 AI 对话框,SSE 流式渲染我踩的几个坑

上周接了个需求:给后台加一个 AI 对话窗口,要求消息一个字一个字往外蹦,像打字机那样。听起来不难,真上手 SSE 才知道坑在哪。记几笔,给后面要做的同学省点时间。

先说背景

后端给的是一个 SSE 接口,前端这边用 Vue3 + <script setup>。我一开始想当然,以为 EventSource 一把梭就完了。结果第一个坑就来了。

坑一:EventSource 不能带 POST,也不好带 header

我们的对话接口要传一长串上下文,得用 POST,还要在 header 里塞鉴权 token。EventSource 只支持 GET,header 也加不了。折腾半天放弃,改用 fetch + ReadableStream 自己读流:

javascript 复制代码
const res = await fetch('/api/chat', {
  method: 'POST',
  headers: { Authorization: `Bearer ${token}` },
  body: JSON.stringify({ messages }),
})
const reader = res.body.getReader()
const decoder = new TextDecoder()
let buffer = ''
while (true) {
  const { done, value } = await reader.read()
  if (done) break
  buffer += decoder.decode(value, { stream: true })
  // 下面要按行切 data: 块
}

坑二:一个 chunk 不等于一条 SSE 消息

我最早天真地拿到 chunk 就 JSON.parse,直接报错。后来打日志才发现,一次 read() 拿到的可能是半条消息,也可能是两条半。SSE 是按 \n\n 分隔事件、data: 开头的,必须自己拿 buffer 缓着按分隔符切,切不出完整的就留在 buffer 里等下一轮。

ini 复制代码
const parts = buffer.split('\n\n')
buffer = parts.pop()  // 最后一段可能不完整,留着
for (const part of parts) {
  const line = part.replace(/^data:\s*/, '')
  if (line === '[DONE]') return
  const json = JSON.parse(line)
  current.value += json.delta  // 追加到响应式变量
}

这个 buffer.pop() 留尾巴的细节,没踩过坑真想不到。

坑三:响应式数组整体替换,DOM 全量重渲染

我一开始每来一个字就 messages.value = [...messages.value] 整个替换,长对话下肉眼可见卡。后来改成只动最后一条消息对象的 content 字段,让 Vue 的细粒度响应只更新那一个节点,顺多了。

一个没做好的地方

中断逻辑我做得很糙。用户点"停止"我只是把 reader cancel 掉,但后端那边其实还在跑,token 照烧。理想做法是发个 abort 信号给后端真正掐断,这块我留了个 TODO 一直没补。

模型这次我直接走的讯飞 MaaS,现成接口调,省得自己部署推理。前端同学接 SSE 还有啥独门技巧,评论区聊聊。

相关推荐
小撒的私房菜2 小时前
Multi-Agent 里谁来指挥?我用一个调度员,让多个 Agent 开始协作
人工智能·后端·agent
拾光人3 小时前
tool calling 没那么玄:我用 Prompt 手搓了一遍
agent
把你拉进白名单3 小时前
7.OpenClaw源码解析——可靠消息投递
人工智能·llm·agent
Oliver_NI3 小时前
Agent 理论(一):Tool Call —— 大模型如何使用工具?
agent
数据智能老司机3 小时前
检索前处理
agent
Solis程序员3 小时前
MCP (Model Context Protocol):AI应用连接外部世界的标准协议
人工智能·microsoft·agent·skill·mcp
贵慜_Derek3 小时前
《从零实现 Agent 系统》连载 29|多 Agent 研究 Harness:Lead、Worker 与 Spawn
人工智能·架构·agent
沉默王二3 小时前
别再写Prompt了!Loop Engineering让Agent自己转起来,一条命令顶你干一天!
agent·ai编程·claude
leeyi3 小时前
Retriever 组件:让 Agent 学会「翻资料」的统一接口
人工智能·后端·agent