【 AI-17 Vue-1 /Lesson57(2025-12-06)】Vue 3 + Vite 实现 LLM 流式输出:从项目搭建到深度解析🌊

2 阅读4分钟

🌊 在当今 AI 应用爆发的时代,前端与大语言模型(LLM)的交互体验成为产品成败的关键。本文将深入剖析一个基于 Vue 3 + Vite 的流式对话系统实现方案,涵盖从项目初始化、核心逻辑编写,到流式响应处理、用户体验优化等全部细节。我们将结合代码实例,全面讲解如何利用 HTML5 的流式读取能力,实现“边生成边显示”的丝滑交互效果。


🚀 项目初始化与技术栈选择

现代前端开发追求高效与简洁。本项目采用 Vite 作为构建工具,搭配 Vue 3<script setup> 语法糖,极大简化了组件开发流程。

初始化命令

根据文档提示,可通过以下任一方式创建项目:

# 方法一:使用官方脚手架(推荐)
npm create vite@latest stream-demo --template vue

# 方法二:传统方式
npm init vite@latest

随后安装依赖并启动开发服务器:

cd stream-demo
npm install
npm run dev

此过程会生成一个标准的 Vue + Vite 项目结构,包含 main.jsApp.vue 等核心文件。


🧠 核心入口:main.js 与应用挂载

main.js 是整个应用的起点:

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'

createApp(App).mount('#app')

这段代码完成了三件事:

  1. vue 包中引入 createApp 工厂函数;
  2. 引入全局样式;
  3. 创建应用实例并将根组件 App.vue 挂载到 index.html 中 id 为 app 的 DOM 节点上。

💡 注意:index.html 文件内容虽简略(仅含 "demo" 字样),但 Vite 会在构建时自动注入必要的 script 和 link 标签,因此无需手动维护。


🖼️ 主界面设计:App.vue 全解析

App.vue 是本项目的核心,它实现了与 LLM 的完整交互流程。

响应式数据声明

使用 Vue 3 的 ref 创建响应式变量:

const question = ref('讲一个喜洋洋和灰太狼的故事,20字')
const stream = ref(true)
const content = ref("")
  • question:用户输入的问题;
  • stream:是否启用流式输出(通过复选框控制);
  • content:LLM 返回的内容,用于实时渲染。

📌 关键概念ref 返回的是一个响应式对象,其内部值通过 .value 访问(在模板中可省略)。

🤖 LLM 请求函数:askLLM

这是整个应用的“大脑”,负责向 DeepSeek API 发起请求并处理响应。

非流式模式(简单场景)

const data = await response.json();
content.value = data.choices[0].message.content;

等待完整响应后一次性赋值。

流式模式(重点!)

流式处理是提升用户体验的核心:

  1. 清空上次结果

    content.value = "";
    
  2. 获取流式读取器

    const reader = response.body?.getReader();
    const decoder = new TextDecoder(); // 将二进制转为 UTF-8 字符串
    
  3. 循环读取数据块(chunk)

    while(!done) {
      const { value, done: doneReading } = await reader?.read()
      // value 是 Uint8Array 类型的二进制数据
    }
    
  4. 解码与拼接

    const chunkValue = buffer + decoder.decode(value, { stream: true });
    

    ⚠️ 注意:{ stream: true } 参数确保在数据不完整时不会抛出错误,而是保留未完成的字节到下一次解码。

  5. 解析 SSE(Server-Sent Events)格式: DeepSeek 的流式响应遵循 data: {...}\n\n 格式。我们按行分割并过滤以 data: 开头的行:

    const lines = chunkValue.split('\n')
      .filter(line => line.startsWith('data: '))
    
  6. 提取 delta 内容并追加

    for (const line of lines) {
      const incoming = line.slice(6); // 去掉 "data: "
      if (incoming === '[DONE]') break; // 流结束标志
      const data = JSON.parse(incoming);
      const delta = data.choices[0].delta.content;
      if (delta) content.value += delta;
    }
    
  7. 缓冲区处理: 若 JSON 解析失败(因数据不完整),将剩余部分存入 buffer,留待下次循环拼接:

    catch(err) {
      buffer += `data: ${incoming}`
    }
    

🔍 为什么需要 buffer?
网络传输的 chunk 边界与 JSON 对象边界不一定对齐。例如,一个完整的 data: {"delta": "hello"} 可能被拆成两个 chunk:data: {"delta": "hello"}。此时首次解析会失败,必须缓存前半部分,与后续数据合并后再解析。


🎨 用户界面与样式

App.vue 的模板部分提供直观的交互:

<div class="input-section">
  <label>输入:</label>
  <input class="input" v-model="question"/>
  <button @click="askLLM">提交</button>
</div>
<div class="output">
  <label>Streaming</label>
  <input type="checkbox" v-model="stream" />
  <div>{{content}}</div>
</div>
  • v-model 实现双向绑定,同步输入框与 question 的值;
  • 复选框控制 stream 状态,切换流式/非流式模式。

样式亮点

  • 背景图background-image: url('./assets/image1.png') 提升视觉体验;
  • 响应式布局:使用 flex 布局确保在不同屏幕尺寸下正常显示;
  • 文字溢出处理word-wrap: break-word 防止长文本溢出容器。

🧪 组件测试:HelloWorld.vue

虽然主功能在 App.vue,但项目仍包含标准的 HelloWorld.vue 组件,用于验证 HMR(热模块替换) 功能:

<template>
  <h1>{{ msg }}</h1>
  <button @click="count++">count is {{ count }}</button>
</template>

修改此文件时,Vite 会自动刷新组件而不重载整个页面,极大提升开发效率。


📚 知识延伸:流式输出的核心价值

正如 readme.md 所强调:

AI 负责效率,我们负责灵魂
streaming: true 边生成边返回,没必要 done 后再返回所有结果

为何流式如此重要?

  1. 降低感知延迟:用户无需等待数秒才能看到第一个字;
  2. 增强互动感:文字逐字出现,模拟“思考”过程,提升拟人化体验;
  3. 节省资源:服务端可边生成边发送,减少内存占用。

技术本质

  • HTML5 Fetch APIresponse.body.getReader() 提供了原生流式读取能力;
  • TextDecoder 正确处理 UTF-8 编码的多字节字符(如中文),避免乱码;
  • SSE 协议 是 LLM 流式输出的事实标准,格式简单可靠。

🛠️ 环境变量与安全

API 密钥通过 Vite 的环境变量机制注入:

'Authorization': `Bearer ${import.meta.env.VITE_DEEPSEEK_API_KEY}`

需在项目根目录创建 .env 文件:

VITE_DEEPSEEK_API_KEY=your_api_key_here

🔒 安全提示.env 文件不应提交到版本控制系统,需加入 .gitignore


✅ 总结

本文完整还原了一个 Vue 3 + Vite + DeepSeek LLM 流式对话系统 的构建过程。从项目初始化、响应式数据管理,到复杂的流式二进制处理、用户体验优化,每一步都体现了现代前端工程的最佳实践。通过深入理解 ReadableStreamTextDecoder 和 SSE 协议,开发者不仅能实现本项目功能,更能举一反三,应用于任何需要实时数据流的场景——无论是 AI 对话、日志监控,还是股票行情推送。

未来,随着 WebTransport 等新技术的普及,前端处理二进制流的能力将进一步增强。但无论技术如何演进, “为用户提供即时反馈” 的核心原则永不过时。