Vue3 + DeepSeek 实战:从零实现AI流式打字机效果
前言:为什么需要流式输出?
在传统的软件开发模式中,我们调用后端API时通常采用“请求-等待-响应”的同步模式。但在大语言模型(LLM)时代,模型生成一段300字的回答可能需要5-10秒。如果让用户全程面对白屏等待,体验是灾难性的。
流式输出(Streaming)正是为了解决这一问题而生。它利用HTTP协议的长连接特性,将AI生成的内容以“打字机”效果逐字推送到前端。
本文将带你使用 Vue3 的组合式API,结合 DeepSeek 强大的模型能力,全从前端视角(无需后端中转)构建一个高性能的AI对话应用。
一、技术选型与环境搭建
1. 核心技术栈
数据格式: Server-Sent Events (SSE) 或标准的 Stream Chunk,DeepSeek API 完全兼容 OpenAI 接口规范。
富文本渲染:
marked.js+highlight.js,用于将Markdown文本和代码块实时渲染为HTML。
2. 获取API Key
前往 DeepSeek 官方平台注册并申请 API Key。由于是前端直接调用,切记不要将API Key硬编码在客户端,但在本地调试或通过服务端代理时,我们需要了解其请求格式:
# 请求示例POST https://api.deepseek.com/v1/chat/completions Content-Type: application/json Authorization: Bearer YOUR_API_KEY
二、核心逻辑实现:流式数据的处理
这是本文最关键的环节。我们需要在前端维持一个 ReadableStream 的读取循环,将二进制流逐步解码并追加到页面上。
1. 定义响应式状态
在 Vue3 的 <script setup> 中,我们定义消息列表和当前正在接收的流式内容。
import { ref, nextTick } from 'vue';const messages = ref([]); // 存储历史对话const currentResponse = ref(''); // 当前AI正在回复的内容const isLoading = ref(false); // 加载状态const abortController = ref(null); // 用于中断请求2. 封装流式请求函数
这是核心逻辑:调用 DeepSeek API,并通过 getReader() 逐块读取数据 。
const fetchDeepSeekStream = async (query) => {
try {
// 建立请求
const response = await fetch('https://api.deepseek.com/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${import.meta.env.VITE_DEEPSEEK_KEY}`
},
body: JSON.stringify({
model: 'deepseek-chat', // 或者 deepseek-reasoner [citation:5]
messages: [
{ role: 'user', content: query }
],
stream: true, // 开启流式是关键
temperature: 0.7
})
});
const reader = response.body.getReader();
const decoder = new TextDecoder('utf-8');
let buffer = ''; // 缓冲区,用于处理不完整的JSON块
while (true) {
const { done, value } = await reader.read();
if (done) break;
// 解码二进制流
const chunk = decoder.decode(value, { stream: true });
const lines = chunk.split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const dataStr = line.replace('data: ', '');
if (dataStr === '[DONE]') {
// 流结束
isLoading.value = false;
break;
}
try {
const json = JSON.parse(dataStr);
const content = json.choices[0].delta?.content;
if (content) {
// 核心:累加响应内容,触发Vue更新视图
currentResponse.value += content;
// 滚动到最新位置
await nextTick();
scrollToBottom();
}
} catch (e) {
// 解析失败可能是数据不完整,暂存到buffer,此处简化处理
console.warn("Parse error", e);
}
}
}
}
} catch (error) {
console.error("Stream error:", error);
isLoading.value = false;
}};3. 组件中的交互逻辑
在用户点击发送时,先将用户消息推入列表,再调用流式函数。
<template>
<div class="chat-box">
<div v-for="msg in messages" class="message">{{ msg.content }}</div>
<!-- 单独渲染流式输出的消息块 -->
<div v-if="currentResponse" class="message assistant">
{{ currentResponse }} <span class="cursor" v-if="isLoading">|</span>
</div>
</div></template>三、进阶体验:Markdown与代码高亮
AI回答的内容通常包含 Markdown 语法,如果直接显示纯文本,代码块会变得杂乱无章。在跨平台开发(如小程序)或 Web 开发中,我们需要引入解析库。
1. 引入marked与highlight.js
import { marked } from 'marked';import hljs from 'highlight.js';// 配置marked解析器marked.setOptions({
highlight: function(code, lang) {
if (lang && hljs.getLanguage(lang)) {
return hljs.highlight(code, { language: lang }).value;
}
return hljs.highlightAuto(code).value;
},
breaks: true});// 在Vue中使用计算属性实时渲染流式内容const renderedMarkdown = computed(() => {
return marked(currentResponse.value);});注意:在微信小程序开发中,无法直接使用 v-html,建议使用 mp-html 或者 ua-markdown 这类专用组件来替代 DOM 操作,以达到更好的兼容性 。
2. 支持深度思考(Reasoner模式)
DeepSeek 提供了 deepseek-reasoner 模型,它会先输出思考链(reasoning_content),再输出最终回答。在前端,我们可以将这两者分开渲染,打造类似 ChatGPT-o1 的体验 。
// 数据结构处理if (json.choices[0].delta?.reasoning_content) {
thinkingText.value += json.choices[0].delta.reasoning_content;} else if (json.choices[0].delta?.content) {
currentResponse.value += json.choices[0].delta.content;}四、扩展至小程序与跨端开发
作为一名合格的软件开发工程师,我们通常需要一套代码覆盖多个平台。利用 UniApp 结合 Vue3,可以将上述 Web 逻辑无缝迁移到小程序中 。
跨端开发的适配要点:
网络请求差异:
Web端:直接使用
fetch或axios。小程序端:UniApp 环境下建议使用
uni.request,但uni.request对ReadableStream的支持不如 Web 完整。如果是小程序,更科学的做法是通过 WebSocket 或者自建云函数来透传流式数据 。UI 组件库:
建议使用
uni-ui配合uv-ui,它们对 Vue3 和暗黑主题有着良好的支持。性能优化:
五、总结与展望
通过以上步骤,我们仅用纯前端技术栈就完成了 Vue3 对 DeepSeek 的流式对接。这套架构不仅适用于 Web 聊天室,稍加改造即可应用于 AI 客服系统、智能代码辅助插件甚至是 RAG 知识库问答系统。
核心收获:
掌握流: 理解了 HTTP 的
ReadableStream是如何在前端被消费的。响应式更新: 利用 Vue3 的
ref特性,currentResponse.value += delta这一行代码就完成了逐字更新。工程化思维: 区分了普通文本传输和 Markdown 渲染的时机,保证了最终界面的美观性。
目前的 AI 应用开发早已不是简单的 API 调用,它考验的是对异步编程、数据结构以及用户体验的深度理解。希望这篇文章能帮助你开启 AI 应用开发的下一步。












冀公网安备