AI 流式输出实践:基于 Vue + DeepSeek 的 Streaming 实现详解
附带流式解析代码、Buffer 解码原理讲解
大语言模型(LLM)已经进入前端开发者的日常,而“响应速度”与“交互体验”则决定了一个 AI 产品是否顺滑可用。传统接口请求需要等待模型生成完整回答后再一次性返回,而 AI 流式输出(Streaming) 则支持 边生成边返回,像 ChatGPT 一样逐字实时显示。这不仅显著减少等待时间,还极大提升交互质感。
流式输出到底解决了什么?
传统模式:
- 请求 → 等待模型生成全部内容 → 一次性返回 → 页面渲染
流式输出(Streaming):
- 模型生成一点发一点 → 前端逐段解析 → 立刻渲染内容
- 体验更贴近实时对话,不再“卡住等结果”
- Tokens 边计算边返回,更省时
一句话总结:
stream = true → 前端可以逐字接收 AI 返回内容,实现自然的打字效果。
Vue 代码示例:核心逻辑版
下面是你提供的核心代码(已梳理注释逻辑),重点关注 ReadableStream + TextDecoder + Buffer 拼接处理 这一段:
import { ref } from 'vue'
// 绑定输入与响应式内容
const question = ref('讲一个喜洋洋和灰太狼的故事,不低于200字')
const stream = ref(true)
const content = ref("")
// 调用大模型
const askLLM = async () => {
if (!question.value) return
content.value = '思考中...'
const response = await fetch('https://api.deepseek.com/chat/completions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${import.meta.env.VITE_DEEPSEEK_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'deepseek-chat',
stream: stream.value,
messages: [{ role: 'user', content: question.value }]
})
})
// --- 进入流式解析模式 ---
if (stream.value) {
content.value = "" // 清空旧内容
const reader = response.body.getReader()
const decoder = new TextDecoder()
let buffer = ''
let done = false
while (!done) {
const { value, done: doneReading } = await reader.read()
done = doneReading
const chunk = buffer + decoder.decode(value)
buffer = '' // 暂存区
// 拆分多行 data: 消息块
const lines = chunk.split('\n').filter(l => l.startsWith('data: '))
for (const line of lines) {
const jsonStr = line.slice(6) // 去掉 "data: "
if (jsonStr === '[DONE]') { done = true; break }
try {
const data = JSON.parse(jsonStr)
const delta = data.choices[0].delta.content
delta && (content.value += delta)
} catch {
// 若 JSON 被截断 → 暂存,等待下一块拼接
buffer += jsonStr
}
}
}
} else {
const data = await response.json()
content.value = data.choices[0].message.content
}
}
上面这段就是 完整 Streaming 实战逻辑,拆解核心思路如下:
流式输出的底层解析流程
Fetch 取回的 body 不是一次性数据,而是可读数据流 ReadableStream
response.body.getReader()—— 获取流读取器reader.read()—— 读取一小块二进制数据(value=Uint8Array)TextDecoder()将二进制转为可读文本- 模型返回采用 SSE(Server-Sent Events)格式 → 按行切割
data: {...} - 解析 JSON 中的
delta.content,逐字更新视图 - 若遇到未完整 JSON → 临时存入 buffer 等待下一块补齐
- 直到收到
[DONE]结束信号
一句话总结:
AI 每吐出一点 token → 前端就读一点 → 解码 → 累积呈现
HTML5 Buffer、编码与 TextDecoder 是什么?
你必须知道两个事实:
- 网络传输不直接传字符串,而是二进制
- AI 输出会被切成若干 chunk(大小与 token、网络状态有关)
因此需要:
| 概念 | 作用 | 对应代码 |
|---|---|---|
| Buffer/Uint8Array | 二进制数据块 | value |
| TextDecoder | 把二进制→文本 | decoder.decode() |
| 暂存区 buffer | 防止JSON截断导致解析失败 | buffer += jsonStr |
为什么要 buffer?
- 假设系统返回一段 JSON 被拆成了两半:
data: {"choices":[{ "delta":{"content":"喜"}}]
data: {"choices":[{ "delta":{"content":"羊"}}]
如果你直接解析第一段 → 报错
所以必须 等待下一段拼接后再 parse
这就是 buffer 的意义!
写在最后
流式输出是构建 AI 应用的核心体验点,它带来的好处显而易见:
- 结果实时呈现,更自然的 AI 对话体验
- 等待时间更短,响应更即时
- 节省带宽,不必一次性返回大文本
- 有助构建 Chat、写作助手、代码生成器等产品
如果你正在做 AI 应用,那么 Streaming 必须掌握。
掌握 ReadableStream + TextDecoder + Buffer 逻辑后,你就能理解大部分 LLM 前端交互实现方式。