前端实现 AI 回复的流式数据(逐字展示效果)
📌 点赞收藏关注不迷路!
你在使用 ChatGPT 时,一定见过这样一个“人性化”的体验:
机器人一边思考一边“打字”回应你,句子一点一点出现。
这其实就是“流式响应数据处理(Streaming Text Response)”的实现效果。
本文将带你深入理解并手把手实现一个流式 AI 回复前端组件👇
🚀 一、什么是“流式响应”?它怎么实现?
在标准 HTTP 请求中,前端只有等服务端全部返回之后,才能使用数据。
而“流式响应”则是服务端一边处理一边返回数据片段(chunk),前端边接收边显示。
它常用的技术有:
技术 | 原理 | 使用场景 |
---|---|---|
SSE | Server-Sent Events,基于 HTTP | 日志/进度/通知推送 |
fetch stream | Fetch 的 Response.body 是流 | ChatGPT 类对话流输出 |
WebSocket | 全双工持久连接 | 双向通信场景 |
🧪 二、实现流式回复的前端核心逻辑(Fetch 版)
async function streamAIAnswer(prompt: string, onData: (text: string) => void) {
const res = await fetch('/api/ai-stream', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ prompt })
})
const reader = res.body?.getReader()
const decoder = new TextDecoder('utf-8')
if (!reader) return
let done = false
while (!done) {
const { value, done: doneReading } = await reader.read()
done = doneReading
const chunk = decoder.decode(value)
onData(chunk) // 每读取一块数据就触发回调
}
}
📌 onData(chunk)
每次都返回一段字符,前端可以累加拼接实现“逐字显示”。
💡 三、封装 Vue Hook:useAIStream()
// useAIStream.ts
import { ref } from 'vue'
export function useAIStream() {
const answer = ref('')
const loading = ref(false)
const ask = async (prompt: string) => {
answer.value = ''
loading.value = true
const res = await fetch('/api/ai-stream', {
method: 'POST',
body: JSON.stringify({ prompt }),
headers: { 'Content-Type': 'application/json' }
})
const reader = res.body?.getReader()
const decoder = new TextDecoder()
if (!reader) return
let done = false
while (!done) {
const { value, done: doneReading } = await reader.read()
done = doneReading
const text = decoder.decode(value)
answer.value += text
}
loading.value = false
}
return {
answer,
loading,
ask
}
}
🎨 四、Vue 实战组件展示(Chat AI)
<template>
<div>
<textarea v-model="prompt" placeholder="请输入问题" />
<button @click="ask(prompt)" :disabled="loading">发送</button>
<div class="answer-box">
<p v-if="loading">AI 正在思考...</p>
<p v-else>{{ answer }}</p>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { useAIStream } from './composables/useAIStream'
const prompt = ref('')
const { answer, loading, ask } = useAIStream()
</script>
样式可以配个逐字动画或滚动条提升体验。
🌐 五、服务端示例(Node + OpenAI)
✅ Node 示例(Express)
import express from 'express'
import { OpenAI } from 'openai'
const app = express()
const openai = new OpenAI({ apiKey: 'YOUR_KEY' })
app.use(express.json())
app.post('/api/ai-stream', async (req, res) => {
res.setHeader('Content-Type', 'text/event-stream')
res.setHeader('Cache-Control', 'no-cache')
res.setHeader('Connection', 'keep-alive')
const completion = await openai.chat.completions.create({
model: 'gpt-3.5-turbo',
messages: [{ role: 'user', content: req.body.prompt }],
stream: true
})
for await (const chunk of completion) {
const text = chunk.choices?.[0]?.delta?.content || ''
res.write(text)
}
res.end()
})
📌 本质:后端使用 stream: true
+ for-await 输出,前端 fetch 逐字读取。
🧠 六、可选增强功能
1. 添加 loading 动画
<p v-if="loading"><span class="typing-cursor">▍</span> AI 正在回复中...</p>
2. 支持停止中断(AbortController)
const controller = new AbortController()
fetch('/api/ai-stream', { signal: controller.signal })
// 在需要中断时 controller.abort()
3. 自动滚动到最新内容
nextTick(() => {
answerBox.value?.scrollIntoView({ behavior: 'smooth' })
})
✅ 最后说一句
实现一个“打字机式的 AI 回复”,只需要一个 fetch + reader.read()
的小技巧。
相比 WebSocket,它更轻更快更稳,非常适合对接 OpenAI / Claude / 自定义推理服务的文本输出。
如果你觉得本文对你有帮助,记得点个 点赞 + 收藏 + 关注!