logo

Vue3流式AI聊天界面开发:Deepseek/OpenAI API对接实战指南

作者:蛮不讲李2025.09.25 23:27浏览量:0

简介:本文详解如何使用Vue3构建仿Deepseek/ChatGPT的流式聊天界面,并完成与Deepseek/OpenAI API的对接,涵盖界面设计、流式响应处理及API调用的完整实现。

一、技术选型与项目初始化

1.1 技术栈选择

Vue3作为前端框架的核心选择,其组合式API与TypeScript支持为复杂交互提供了良好基础。搭配Vite构建工具可显著提升开发效率,而Pinia作为状态管理库能简化跨组件通信。后端API对接需支持SSE(Server-Sent Events)协议,这是实现流式响应的关键技术。

1.2 项目初始化流程

  1. npm create vue@latest ai-chat-demo -- --template vue3-ts
  2. cd ai-chat-demo
  3. npm install pinia axios @vueuse/core

项目结构建议采用模块化设计:

  1. src/
  2. ├── api/ # API请求封装
  3. ├── components/ # 通用组件
  4. ├── composables/ # 组合式函数
  5. ├── router/ # 路由配置
  6. ├── stores/ # Pinia状态管理
  7. └── views/ # 页面组件

二、流式聊天界面实现

2.1 核心组件设计

消息气泡组件需支持双向显示,通过props传递消息类型与内容:

  1. <!-- MessageBubble.vue -->
  2. <template>
  3. <div :class="['bubble', { 'user': isUser }]">
  4. <div v-if="!isStreaming" class="content">{{ content }}</div>
  5. <div v-else class="streaming">
  6. <span v-for="i in 5" :key="i" class="dot"></span>
  7. </div>
  8. </div>
  9. </template>
  10. <style scoped>
  11. .bubble {
  12. max-width: 70%;
  13. margin: 8px;
  14. padding: 12px;
  15. border-radius: 18px;
  16. }
  17. .user {
  18. margin-left: auto;
  19. background: #4a6bff;
  20. color: white;
  21. }
  22. .streaming .dot {
  23. display: inline-block;
  24. width: 8px;
  25. height: 8px;
  26. border-radius: 50%;
  27. background: #fff;
  28. animation: bounce 1.4s infinite ease-in-out;
  29. }
  30. /* 省略动画细节 */
  31. </style>

2.2 消息流处理机制

采用EventSource实现SSE连接,需处理连接中断与重试逻辑:

  1. // api/chat.ts
  2. export const streamChat = async (prompt: string) => {
  3. const eventSource = new EventSource(`/api/chat/stream?prompt=${encodeURIComponent(prompt)}`);
  4. return new Promise<string[]>((resolve, reject) => {
  5. const chunks: string[] = [];
  6. eventSource.onmessage = (event) => {
  7. const data = JSON.parse(event.data);
  8. if (data.finish_reason) {
  9. eventSource.close();
  10. resolve(chunks);
  11. } else {
  12. chunks.push(data.text);
  13. }
  14. };
  15. eventSource.onerror = (error) => {
  16. eventSource.close();
  17. reject(error);
  18. };
  19. });
  20. };

三、Deepseek/OpenAI API对接

3.1 API请求封装

创建统一的API服务层,处理认证与错误重试:

  1. // api/service.ts
  2. import axios from 'axios';
  3. const apiClient = axios.create({
  4. baseURL: import.meta.env.VITE_API_BASE_URL,
  5. timeout: 30000,
  6. });
  7. apiClient.interceptors.request.use((config) => {
  8. const token = localStorage.getItem('api_token');
  9. if (token) {
  10. config.headers.Authorization = `Bearer ${token}`;
  11. }
  12. return config;
  13. });
  14. export const callApi = async <T>(url: string, config?: AxiosRequestConfig) => {
  15. try {
  16. const response = await apiClient.get<T>(url, config);
  17. return response.data;
  18. } catch (error) {
  19. if (axios.isAxiosError(error) && error.response?.status === 401) {
  20. // 处理认证错误
  21. }
  22. throw error;
  23. }
  24. };

3.2 流式响应处理

Deepseek API的流式响应格式与OpenAI类似,但存在字段差异:

  1. // 处理Deepseek响应
  2. const processDeepseekStream = (event: MessageEvent) => {
  3. const data = JSON.parse(event.data);
  4. // Deepseek可能使用不同字段名
  5. const text = data.result?.content || data.choices[0]?.text;
  6. if (text) appendMessage(text);
  7. };
  8. // 处理OpenAI响应
  9. const processOpenAIStream = (event: MessageEvent) => {
  10. const data = JSON.parse(event.data);
  11. const delta = data.choices[0]?.delta?.content;
  12. if (delta) appendMessage(delta);
  13. };

四、性能优化与用户体验

4.1 虚拟滚动实现

当消息量超过50条时,使用虚拟滚动提升性能:

  1. <!-- ChatContainer.vue -->
  2. <template>
  3. <div ref="container" class="chat-container">
  4. <div :style="{ height: totalHeight + 'px' }" class="scroll-wrapper">
  5. <div :style="{ transform: `translateY(${offset}px)` }" class="content">
  6. <MessageBubble
  7. v-for="(msg, index) in visibleMessages"
  8. :key="index"
  9. :content="msg.content"
  10. :is-user="msg.isUser"
  11. />
  12. </div>
  13. </div>
  14. </div>
  15. </template>
  16. <script setup>
  17. const container = ref<HTMLElement>();
  18. const visibleCount = 20; // 可见消息数
  19. const messages = ref<Array<{content: string, isUser: boolean}>>([]);
  20. const visibleMessages = computed(() => {
  21. const start = Math.max(0, messages.value.length - visibleCount);
  22. return messages.value.slice(start);
  23. });
  24. </script>

4.2 错误处理机制

实现三级错误处理体系:

  1. 网络层:重试机制(最多3次)
  2. API层:统一错误码处理
  3. 界面层:用户友好的错误提示
  1. // composables/useError.ts
  2. export const useError = () => {
  3. const error = ref<string | null>(null);
  4. const handleApiError = (e: unknown) => {
  5. if (axios.isAxiosError(e)) {
  6. switch (e.response?.status) {
  7. case 401: error.value = '请重新登录'; break;
  8. case 429: error.value = '请求过于频繁,请稍后再试'; break;
  9. default: error.value = '服务暂时不可用';
  10. }
  11. } else {
  12. error.value = '网络连接异常';
  13. }
  14. setTimeout(() => error.value = null, 3000);
  15. };
  16. return { error, handleApiError };
  17. };

五、部署与扩展建议

5.1 环境变量配置

  1. # .env.production
  2. VITE_API_BASE_URL=https://api.deepseek.com/v1
  3. VITE_API_TYPE=deepseek # 或openai
  4. VITE_API_KEY=your_api_key

5.2 多模型支持实现

通过策略模式实现API适配:

  1. // api/modelStrategy.ts
  2. interface ChatModel {
  3. streamChat(prompt: string): Promise<string[]>;
  4. }
  5. class DeepseekModel implements ChatModel {
  6. async streamChat(prompt: string) {
  7. // Deepseek特定实现
  8. }
  9. }
  10. class OpenAIModel implements ChatModel {
  11. async streamChat(prompt: string) {
  12. // OpenAI特定实现
  13. }
  14. }
  15. export const createModel = (type: string): ChatModel => {
  16. switch (type) {
  17. case 'deepseek': return new DeepseekModel();
  18. case 'openai': return new OpenAIModel();
  19. default: throw new Error('Unsupported model');
  20. }
  21. };

5.3 安全增强措施

  1. 输入过滤:使用DOMPurify防止XSS攻击
  2. 速率限制:前端实现简单限流
  3. 数据加密:敏感操作使用HTTPS
  1. // utils/security.ts
  2. import DOMPurify from 'dompurify';
  3. export const sanitizeInput = (text: string) => {
  4. return DOMPurify.sanitize(text, { ALLOWED_TAGS: [] });
  5. };
  6. export const debounce = <T extends (...args: any[]) => any>(
  7. func: T,
  8. wait: number
  9. ) => {
  10. let timeout: NodeJS.Timeout;
  11. return (...args: Parameters<T>) => {
  12. clearTimeout(timeout);
  13. timeout = setTimeout(() => func(...args), wait);
  14. };
  15. };

六、完整实现示例

6.1 主组件整合

  1. <!-- ChatView.vue -->
  2. <template>
  3. <div class="chat-wrapper">
  4. <div class="message-list" ref="messageList">
  5. <MessageBubble
  6. v-for="(msg, index) in messages"
  7. :key="index"
  8. :content="msg.content"
  9. :is-user="msg.isUser"
  10. :is-streaming="index === messages.length - 1 && isStreaming"
  11. />
  12. </div>
  13. <div class="input-area">
  14. <textarea v-model="input" @keydown.enter.prevent="handleSubmit" />
  15. <button @click="handleSubmit" :disabled="isLoading">
  16. {{ isLoading ? '发送中...' : '发送' }}
  17. </button>
  18. </div>
  19. </div>
  20. </template>
  21. <script setup>
  22. import { ref, onMounted } from 'vue';
  23. import { useChatStore } from '@/stores/chat';
  24. import { createModel } from '@/api/modelStrategy';
  25. const chatStore = useChatStore();
  26. const input = ref('');
  27. const isLoading = ref(false);
  28. const isStreaming = ref(false);
  29. const messages = ref<Array<{content: string, isUser: boolean}>>([]);
  30. const model = createModel(import.meta.env.VITE_API_TYPE);
  31. const handleSubmit = async () => {
  32. if (!input.value.trim()) return;
  33. const userMsg = { content: input.value, isUser: true };
  34. messages.value.push(userMsg);
  35. input.value = '';
  36. isLoading.value = true;
  37. isStreaming.value = true;
  38. try {
  39. const chunks = await model.streamChat(userMsg.content);
  40. const aiMsg = { content: chunks.join(''), isUser: false };
  41. messages.value.push(aiMsg);
  42. } catch (error) {
  43. console.error('Chat error:', error);
  44. } finally {
  45. isLoading.value = false;
  46. isStreaming.value = false;
  47. }
  48. };
  49. </script>

6.2 状态管理实现

  1. // stores/chat.ts
  2. import { defineStore } from 'pinia';
  3. export const useChatStore = defineStore('chat', {
  4. state: () => ({
  5. messages: [] as Array<{content: string, isUser: boolean}>,
  6. isLoading: false,
  7. }),
  8. actions: {
  9. addMessage(message: string, isUser: boolean) {
  10. this.messages.push({ content: message, isUser });
  11. },
  12. clearMessages() {
  13. this.messages = [];
  14. },
  15. },
  16. });

七、总结与展望

本实现方案通过Vue3的组合式API构建了响应式的流式聊天界面,采用策略模式实现了对Deepseek和OpenAI API的灵活适配。关键优化点包括:

  1. 虚拟滚动技术处理大量消息
  2. 三级错误处理体系提升稳定性
  3. 环境变量配置实现多环境部署

未来可扩展方向:

  1. 增加多轮对话上下文管理
  2. 实现消息编辑与删除功能
  3. 添加插件系统支持自定义功能
  4. 集成语音输入输出功能

通过模块化设计和清晰的分层架构,该方案可快速适配其他LLM服务,为开发者提供高可复用的AI聊天界面解决方案。

相关文章推荐

发表评论