简介:本文详细解析如何使用Vue3构建仿Deepseek/ChatGPT的流式聊天AI界面,并完成与Deepseek/OpenAI API的对接,涵盖界面设计、流式响应处理、错误管理及性能优化等关键环节。
在构建流式聊天AI界面时,技术栈的选择直接影响开发效率与用户体验。Vue3凭借其组合式API、响应式系统及TypeScript支持,成为实现复杂交互逻辑的理想框架。结合Vite构建工具,可显著提升开发体验与热更新效率。
核心架构设计:
ChatContainer(容器层)、MessageList(消息列表)、InputBar(输入框)及StreamTyping(流式打字效果)等独立组件,通过Props与Emits实现数据流通。ApiService类,统一处理Deepseek/OpenAI API的请求、重试机制及错误拦截。流式响应(Streaming Response)是AI聊天界面的核心特性,需通过WebSocket或分块传输编码(Chunked Transfer Encoding)实现。以下以OpenAI的/v1/chat/completions流式接口为例,分步骤解析实现逻辑。
关键代码:
// src/services/api.tsimport { ChatCompletionRequestMessage, CreateChatCompletionResponse } from 'openai';export class OpenAIService {private apiKey: string;private baseUrl = 'https://api.openai.com/v1';constructor(apiKey: string) {this.apiKey = apiKey;}async streamChat(messages: ChatCompletionRequestMessage[]) {const response = await fetch(`${this.baseUrl}/chat/completions`, {method: 'POST',headers: {'Content-Type': 'application/json','Authorization': `Bearer ${this.apiKey}`,},body: JSON.stringify({model: 'gpt-3.5-turbo',messages,stream: true, // 启用流式响应}),});if (!response.ok) throw new Error('API请求失败');const reader = response.body?.getReader();if (!reader) throw new Error('无法读取响应流');return new ReadableStream({async start(controller) {try {while (true) {const { done, value } = await reader.read();if (done) break;const chunk = new TextDecoder().decode(value);const lines = chunk.split('\n').filter(line => line.trim());for (const line of lines) {if (!line.startsWith('data: ')) continue;const data = line.replace('data: ', '').trim();if (data === '[DONE]') break;try {const parsed = JSON.parse(data);const delta = parsed.choices[0].delta?.content || '';if (delta) controller.enqueue(delta);} catch (e) {console.error('解析流数据失败:', e);}}}controller.close();} catch (error) {controller.error(error);}},});}}
逻辑说明:
fetch发起请求,设置stream: true启用流式传输。ReadableStream逐块处理响应数据,解析每行中的delta.content(增量文本)。[DONE]标记,仅推送有效文本片段。在Vue组件中,通过ref与watchEffect监听流式数据,实现打字机效果:
<!-- src/components/StreamTyping.vue --><template><div class="typing-container"><span v-for="(char, index) in visibleChars" :key="index">{{ char }}</span><span class="cursor" v-if="isStreaming">|</span></div></template><script setup>import { ref, watchEffect } from 'vue';const props = defineProps({stream: { type: ReadableStream, required: true },});const fullText = ref('');const visibleChars = ref([]);const isStreaming = ref(false);watchEffect(async () => {isStreaming.value = true;const reader = props.stream.getReader();try {while (true) {const { done, value } = await reader.read();if (done) break;fullText.value += value;// 模拟逐字符显示(实际可根据需求调整)visibleChars.value = fullText.value.split('');}} finally {isStreaming.value = false;}});</script>
gpt-3.5-turbo(低成本)或gpt-4(高精度)。
// src/utils/errorHandler.tsexport const handleApiError = (error: unknown) => {if (error instanceof Error) {if (error.message.includes('429')) {return { type: 'RETRY', message: '请求过于频繁,请稍后重试' };}if (error.message.includes('401')) {return { type: 'AUTH', message: 'API密钥无效' };}}return { type: 'UNKNOWN', message: '服务暂时不可用' };};
vue-virtual-scroller,减少DOM节点数量。localStorage存储最近对话,提升冷启动速度。完成开发后,通过以下步骤部署:
vite-plugin-compression生成Gzip压缩包。通过本文的方案,开发者可快速构建一个支持流式响应的AI聊天界面,并灵活对接不同后端服务。实际项目中,建议结合单元测试(Vitest)与E2E测试(Cypress)确保稳定性。