前端实现 AI 回复的流式数据(逐字展示效果)

前端实现 AI 回复的流式数据(逐字展示效果)

📌 点赞收藏关注不迷路!
你在使用 ChatGPT 时,一定见过这样一个“人性化”的体验:

机器人一边思考一边“打字”回应你,句子一点一点出现。

这其实就是“流式响应数据处理(Streaming Text Response)”的实现效果。

本文将带你深入理解并手把手实现一个流式 AI 回复前端组件👇


🚀 一、什么是“流式响应”?它怎么实现?

在标准 HTTP 请求中,前端只有等服务端全部返回之后,才能使用数据。

而“流式响应”则是服务端一边处理一边返回数据片段(chunk),前端边接收边显示

它常用的技术有:

技术原理使用场景
SSEServer-Sent Events,基于 HTTP日志/进度/通知推送
fetch streamFetch 的 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 / 自定义推理服务的文本输出。

如果你觉得本文对你有帮助,记得点个 点赞 + 收藏 + 关注

在Vue前端中,想要实现类似AI对话那样实时流式展示获取的数据,可以采用Fetch API发起网络请求,并结合`TextDecoder`处理文本数据。以下是基本步骤: 1. **导入依赖**: 首先,在你的Vue组件里需要导入`fetch`函数(ES6语法下通常默认可用),如果在旧版本中需要引入,可以这样做: ```javascript import fetch from 'isomorphic-fetch'; ``` 2. **发送异步请求**: 使用`fetch`发起GET或POST请求,这里假设你想从服务器获取JSON格式的文本数据: ```javascript async function fetchData(url) { try { const response = await fetch(url); if (!response.ok) { throw new Error(`Error ${response.status}: ${response.statusText}`); } const text = await response.text(); // 获取响应体的纯文本内容 } catch (error) { console.error('Error fetching data:', error); } return text; } ``` 3. **解码文本**: `TextDecoder`用于解码文本数据,尤其是对于包含非ASCII字符的数据: ```javascript const decoder = new TextDecoder(); let decodedData = ''; fetchData(url).then(text => { const chunks = text.split(''); for (let chunk of chunks) { decodedData += decoder.decode(new Uint8Array([chunk.charCodeAt(0)])); // 逐字节解码 // 可能的话,你可以在这里添加一个事件监听,每次新数据到达就更新DOM显示 if (decodedData.endsWith('\n')) { // 检测是否结束了一行 updateDisplay(decodedData); // 更新UI展示 decodedData = ''; // 清空已处理的部分 } } }); ``` 4. **更新DOM**: 创建一个方法如`updateDisplay()`来更新页面上的文本区域或聊天框,将解码后的文本插入到相应位置。 5. **错误处理**: 当数据流中断或者解析过程中出错,记得清除已经接收到的内容并处理异常。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值