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 还有啥独门技巧,评论区聊聊。

相关推荐
李燚7 小时前
RAG 流水线设计:Eino 的 Loader → Transformer → Indexer → Retriever(第60篇-E46)
人工智能·深度学习·transformer·agent·rag·aiagent·eino
To_OC7 小时前
跟 AI 写代码越写越乱?我靠这套「Vibe Coding」思路彻底治好了幻觉屎山
人工智能·agent·vibecoding
字节跳动开源11 小时前
火山引擎开源 Agent 驱动的搜索自迭代技术
数据库·开源·agent
阿里云大数据AI技术12 小时前
阿里云 Elasticsearch 9.4 Agent Builder 实战
人工智能·elasticsearch·agent
明明如月学长12 小时前
Skill、Agent 和 Subagent 的区别是什么?我用大白话讲清楚
agent
uccs13 小时前
把工具组装成应用:代码分析、Research Agent、Vibe Coding
agent·ai编程·claude
扯蛋43813 小时前
从脱敏到审批:我用 LangChain 11 个中间件给羽毛球 AI 助手装了一整套"安全阀"
langchain·llm·agent
DigitalOcean15 小时前
Claude Opus 5 现已上线 DigitalOcean AI 推理云
agent
沉默王二15 小时前
腾讯面试官:“你说你做了一个终端Agent,那说说 LLM 和 Agent的区别,ReAct、MCP、Tool、Memory、Skills?”我信誓旦旦开始背了
面试·agent·腾讯
后端小肥肠16 小时前
做个人 IP 不用真人出镜,我做了个一键生成 IP 动画视频的 Skill
人工智能·aigc·agent