Mirage Flow在Vue3项目中的集成实战:前端AI应用开发
想在前端项目里加点“智能”吗?比如让电商网站能自动推荐商品,或者让内容平台帮你生成摘要?以前这活儿得后端配合,现在有了像Mirage Flow这样的大模型,前端自己也能搞定了。
今天咱们就来聊聊,怎么把一个AI大模型,顺滑地集成到你的Vue3项目里。我会手把手带你走一遍,从怎么封装一个好用又好看的AI对话组件,到怎么优化API调用让它又快又稳,最后再给你一个电商推荐系统的完整例子。整个过程,咱们就用大白话讲,保证你跟着做就能跑起来。
1. 环境准备与项目搭建
在开始写代码之前,咱们得先把“舞台”搭好。这里假设你已经有了一个Vue3项目,如果没有,用Vite新建一个也很快。
1.1 获取Mirage Flow的访问凭证
首先,你需要一个能和Mirage Flow对话的“钥匙”。这通常是一个API Key。
- 访问Mirage Flow的官方平台,注册并登录你的账号。
- 在控制台或开发者设置页面,找到创建API Key的选项。
- 创建一个新的Key,并妥善保存它。这个Key一旦生成,通常只显示一次,所以最好直接复制到安全的地方。
安全提示:这个API Key就像你的密码,千万不要直接硬编码在客户端的代码里提交到GitHub等公开仓库。我们稍后会讲到如何在Vue项目中安全地管理它。
1.2 安装必要的依赖
在你的Vue3项目根目录下,打开终端,安装我们需要的包。核心是用于网络请求的axios。
npm install axios # 或者使用 yarn yarn add axios如果你打算使用Pinia进行状态管理(推荐,会让代码更清晰),也可以一并安装:
npm install pinia好了,基础准备完成,接下来我们进入正题,看看怎么在Vue组件里和AI“聊天”。
2. 封装可复用的AI对话组件
直接在每个页面里写一堆调用AI的代码,既麻烦又难维护。最好的办法是把它封装成一个独立的、像普通UI组件一样好用的东西。
2.1 创建基础的对话组件
我们先创建一个最简单的组件,它能输入文字,发送给Mirage Flow,然后显示AI的回复。
在src/components目录下,新建一个MirageChat.vue文件:
<template> <div class="mirage-chat"> <div class="chat-history"> <div v-for="(msg, index) in messages" :key="index" :class="['message', msg.role]" > <strong>{{ msg.role === 'user' ? '你' : 'AI' }}:</strong> {{ msg.content }} </div> </div> <div class="input-area"> <textarea v-model="userInput" placeholder="输入你想问的问题..." @keydown.enter.exact.prevent="sendMessage" /> <button @click="sendMessage" :disabled="isLoading"> {{ isLoading ? '思考中...' : '发送' }} </button> </div> </div> </template> <script setup> import { ref } from 'vue'; import axios from 'axios'; // 消息列表,每条消息有角色(user/assistant)和内容 const messages = ref([]); // 用户输入框的内容 const userInput = ref(''); // 加载状态,防止重复发送 const isLoading = ref(false); // 你的Mirage Flow API Key(注意:这只是演示,实际要用环境变量) const API_KEY = '你的_实际_API_KEY_放在环境变量里'; const API_URL = 'https://api.mirage-flow.com/v1/chat/completions'; // 示例端点,请替换为实际地址 const sendMessage = async () => { if (!userInput.value.trim() || isLoading.value) return; // 把用户输入添加到消息列表 const userMessage = { role: 'user', content: userInput.value }; messages.value.push(userMessage); const currentInput = userInput.value; userInput.value = ''; // 清空输入框 isLoading.value = true; try { const response = await axios.post(API_URL, { model: 'mirage-flow-latest', // 指定模型名称 messages: [...messages.value], // 发送全部历史记录,让AI有上下文 stream: false, // 先不用流式响应 }, { headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json', } }); // 将AI回复添加到消息列表 const aiReply = response.data.choices[0].message; messages.value.push(aiReply); } catch (error) { console.error('调用AI接口失败:', error); messages.value.push({ role: 'assistant', content: '抱歉,我好像出错了,请稍后再试。' }); } finally { isLoading.value = false; } }; </script> <style scoped> .mirage-chat { border: 1px solid #e0e0e0; border-radius: 8px; padding: 20px; max-width: 600px; margin: 0 auto; } .chat-history { min-height: 300px; max-height: 400px; overflow-y: auto; margin-bottom: 20px; padding: 10px; background: #f9f9f9; border-radius: 4px; } .message { margin-bottom: 10px; padding: 8px 12px; border-radius: 6px; } .message.user { background-color: #e3f2fd; text-align: right; } .message.assistant { background-color: #f5f5f5; } .input-area { display: flex; gap: 10px; } textarea { flex: 1; padding: 10px; border: 1px solid #ccc; border-radius: 4px; resize: vertical; min-height: 60px; font-family: inherit; } button { padding: 10px 20px; background-color: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; } button:disabled { background-color: #cccccc; cursor: not-allowed; } </style>现在,你可以在任何Vue页面里像使用普通组件一样使用它了:
<template> <div> <h1>我的AI助手</h1> <MirageChat /> </div> </template> <script setup> import MirageChat from '@/components/MirageChat.vue'; </script>2.2 使用环境变量保护API Key
刚才我们把API Key直接写在了组件里,这非常不安全。正确做法是使用环境变量。
在项目根目录创建
.env.local文件(确保它在.gitignore里):VITE_MIRAGE_FLOW_API_KEY=你的_实际_API_KEY VITE_MIRAGE_FLOW_API_URL=https://api.mirage-flow.com/v1(注意:Vite要求客户端可访问的环境变量必须以
VITE_开头)修改组件中的代码,通过
import.meta.env读取环境变量:// 替换之前的硬编码 const API_KEY = import.meta.env.VITE_MIRAGE_FLOW_API_KEY; const API_BASE_URL = import.meta.env.VITE_MIRAGE_FLOW_API_URL; const API_URL = `${API_BASE_URL}/chat/completions`;
这样,你的敏感信息就不会暴露在代码仓库中了。
3. 优化API调用与状态管理
基础功能有了,但一个健壮的应用还需要考虑更多:比如加载状态、错误处理、请求取消,以及如何在不同组件间共享AI的响应状态。这时候,Pinia状态管理库就派上用场了。
3.1 创建AI专用的Store
我们用Pinia创建一个Store,集中管理所有和Mirage Flow交互的逻辑和状态。
创建src/stores/mirageStore.js:
import { defineStore } from 'pinia'; import { ref } from 'vue'; import axios from 'axios'; export const useMirageStore = defineStore('mirage', () => { // 状态 const conversation = ref([]); // 当前会话消息 const isLoading = ref(false); const error = ref(null); // 获取配置 const API_KEY = import.meta.env.VITE_MIRAGE_FLOW_API_KEY; const API_BASE_URL = import.meta.env.VITE_MIRAGE_FLOW_API_URL; // 用于取消请求的CancelToken源 let cancelTokenSource = null; // 动作(Actions) const sendMessage = async (userMessage, options = {}) => { if (isLoading.value) { cancelCurrentRequest(); // 如果正在请求,先取消 } // 添加用户消息到会话 conversation.value.push({ role: 'user', content: userMessage }); isLoading.value = true; error.value = null; // 创建新的取消令牌 cancelTokenSource = axios.CancelToken.source(); const requestMessages = options.useHistory ? [...conversation.value] : [{ role: 'user', content: userMessage }]; const requestBody = { model: options.model || 'mirage-flow-latest', messages: requestMessages, max_tokens: options.maxTokens || 500, temperature: options.temperature || 0.7, ...options.extraParams // 允许传入其他参数 }; try { const response = await axios.post( `${API_BASE_URL}/chat/completions`, requestBody, { headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json', }, cancelToken: cancelTokenSource.token } ); const aiMessage = response.data.choices[0].message; conversation.value.push(aiMessage); return aiMessage; } catch (err) { if (axios.isCancel(err)) { console.log('请求被取消:', err.message); } else { console.error('API调用错误:', err); error.value = err.response?.data?.error?.message || '网络请求失败'; // 可以选择移除刚才添加的用户消息,或者保留并显示错误 // conversation.value.pop(); } throw err; // 将错误抛给调用者 } finally { isLoading.value = false; cancelTokenSource = null; } }; const cancelCurrentRequest = () => { if (cancelTokenSource) { cancelTokenSource.cancel('用户取消了请求'); } }; const clearConversation = () => { conversation.value = []; error.value = null; cancelCurrentRequest(); }; // 暴露给组件使用的状态和方法 return { conversation, isLoading, error, sendMessage, cancelCurrentRequest, clearConversation, }; });3.2 在组件中使用Store
现在,我们可以重构之前的MirageChat.vue组件,让它使用Store,这样逻辑更清晰,状态也更容易在其他组件共享。
<template> <div class="mirage-chat"> <div class="chat-history"> <div v-for="(msg, index) in mirageStore.conversation" :key="index" :class="['message', msg.role]" > <strong>{{ msg.role === 'user' ? '你' : 'AI' }}:</strong> {{ msg.content }} </div> <div v-if="mirageStore.error" class="error"> 错误: {{ mirageStore.error }} </div> </div> <div class="input-area"> <textarea v-model="userInput" placeholder="输入你想问的问题..." @keydown.enter.exact.prevent="handleSend" /> <div class="button-group"> <button @click="handleSend" :disabled="mirageStore.isLoading"> {{ mirageStore.isLoading ? '思考中...' : '发送' }} </button> <button @click="mirageStore.clearConversation" class="secondary"> 清空对话 </button> </div> </div> </div> </template> <script setup> import { ref } from 'vue'; import { useMirageStore } from '@/stores/mirageStore'; const mirageStore = useMirageStore(); const userInput = ref(''); const handleSend = async () => { if (!userInput.value.trim()) return; try { await mirageStore.sendMessage(userInput.value, { useHistory: true }); userInput.value = ''; } catch (error) { // 错误已在Store中处理,这里可以做一些UI上的额外反馈 console.log('发送消息时捕获到错误:', error); } }; </script> <style scoped> /* 样式同上,略作调整 */ .button-group { display: flex; flex-direction: column; gap: 5px; } button.secondary { background-color: #6c757d; } .error { color: #dc3545; padding: 10px; background-color: #f8d7da; border-radius: 4px; margin-top: 10px; } </style>看,现在组件的逻辑清爽多了,所有和API交互的细节都被Store隐藏了起来。而且,项目里任何其他组件,只要引入这个Store,就能读取或操作同一份对话状态。
4. 实战案例:构建一个智能电商推荐系统
光有聊天组件还不够酷,我们把它用在一个真实的场景里。假设我们有一个电商网站,想根据用户正在浏览的商品,实时推荐相关的搭配或替代品。
4.1 设计推荐交互界面
我们创建一个新的页面组件ProductRecommendation.vue。它左边展示商品详情,右边就是一个根据该商品信息进行推荐的AI区域。
<template> <div class="product-page"> <!-- 左侧商品信息 --> <div class="product-detail"> <img :src="currentProduct.image" :alt="currentProduct.name" /> <h2>{{ currentProduct.name }}</h2> <p class="price">{{ currentProduct.price }}</p> <p class="description">{{ currentProduct.description }}</p> <button @click="addToCart">加入购物车</button> </div> <!-- 右侧AI推荐区 --> <div class="ai-recommendation"> <h3> AI购物助手</h3> <p>正在分析“{{ currentProduct.name }}”...</p> <div class="recommendation-actions"> <button @click="getRecommendation('complement')" :disabled="isLoading" > 推荐搭配商品 </button> <button @click="getRecommendation('alternative')" :disabled="isLoading" > 寻找类似商品 </button> <button @click="getRecommendation('upgrade')" :disabled="isLoading" > 推荐升级选项 </button> </div> <!-- 推荐结果展示 --> <div v-if="recommendationResult" class="result-box"> <h4>为您推荐:</h4> <p>{{ recommendationResult }}</p> </div> <!-- 加载和错误状态 --> <div v-if="isLoading" class="loading">AI正在思考...</div> <div v-if="error" class="error">{{ error }}</div> <!-- 你也可以把之前的聊天组件嵌入这里,进行更自由的问答 --> <div class="quick-chat"> <h4>快速提问</h4> <input v-model="quickQuestion" placeholder="例如:这个商品适合送礼吗?" @keydown.enter="askQuickQuestion" /> <button @click="askQuickQuestion">提问</button> <p v-if="quickAnswer" class="quick-answer"><strong>答:</strong>{{ quickAnswer }}</p> </div> </div> </div> </template> <script setup> import { ref, computed } from 'vue'; import { useMirageStore } from '@/stores/mirageStore'; // 模拟当前浏览的商品数据 const currentProduct = ref({ id: 1, name: '无线降噪耳机', price: '¥899', description: '旗舰级主动降噪,长达30小时续航,高保真音质,佩戴舒适。', image: '/images/headphone.jpg' }); const mirageStore = useMirageStore(); const recommendationResult = ref(''); const quickQuestion = ref(''); const quickAnswer = ref(''); // 从Store派生出的本地状态,方便模板使用 const isLoading = computed(() => mirageStore.isLoading); const error = computed(() => mirageStore.error); const getRecommendation = async (type) => { recommendationResult.value = ''; // 清空之前的结果 quickAnswer.value = ''; // 清空快速问答 // 构建给AI的提示词,清晰说明任务和商品信息 const promptMap = { complement: `我正在浏览一款商品:“${currentProduct.value.name}”。${currentProduct.value.description}。请为我推荐2-3款可以与之搭配使用的商品,并简要说明推荐理由。请用简洁的列表形式回复。`, alternative: `我正在考虑购买:“${currentProduct.value.name}”。但我想看看市场上其他类似的选项。请推荐2-3款同类型、同价位的竞品,并客观对比它们的优缺点。`, upgrade: `我对“${currentProduct.value.name}”感兴趣,但想了解是否有更高端或更具特色的升级版本或同类高端产品。请推荐1-2款,并说明它们“升级”在何处。` }; const userPrompt = promptMap[type] || promptMap.complement; try { // 这里我们不让AI看到历史对话,每次都是独立的新推荐 const aiMessage = await mirageStore.sendMessage(userPrompt, { useHistory: false }); recommendationResult.value = aiMessage.content; } catch (err) { // 错误已由Store处理,这里可以设置一个兜底的展示信息 recommendationResult.value = '暂时无法获取推荐,请稍后重试。'; } }; const askQuickQuestion = async () => { if (!quickQuestion.value.trim()) return; const fullQuestion = `关于商品“${currentProduct.value.name}”(${currentProduct.value.description}),我的问题是:${quickQuestion.value}`; try { const aiMessage = await mirageStore.sendMessage(fullQuestion, { useHistory: false }); quickAnswer.value = aiMessage.content; quickQuestion.value = ''; } catch (err) { quickAnswer.value = '回答生成失败,请检查网络或稍后再试。'; } }; const addToCart = () => { // 加入购物车逻辑 console.log('已添加:', currentProduct.value.name); }; </script> <style scoped> .product-page { display: grid; grid-template-columns: 1fr 1fr; gap: 40px; max-width: 1200px; margin: 30px auto; padding: 20px; } .product-detail, .ai-recommendation { padding: 25px; border-radius: 12px; background: white; box-shadow: 0 4px 12px rgba(0,0,0,0.08); } .product-detail img { width: 100%; border-radius: 8px; margin-bottom: 20px; } .recommendation-actions { display: flex; gap: 10px; margin: 20px 0; flex-wrap: wrap; } .recommendation-actions button { padding: 12px 18px; background-color: #28a745; color: white; border: none; border-radius: 6px; cursor: pointer; flex: 1; min-width: 140px; } .recommendation-actions button:hover { background-color: #218838; } .result-box { margin-top: 25px; padding: 20px; background-color: #f8f9fa; border-left: 4px solid #007bff; border-radius: 4px; } .quick-chat { margin-top: 30px; padding-top: 20px; border-top: 1px dashed #dee2e6; } .quick-chat input { width: 70%; padding: 10px; margin-right: 10px; border: 1px solid #ced4da; border-radius: 4px; } .loading, .error { padding: 15px; margin: 15px 0; border-radius: 4px; } .loading { background-color: #fff3cd; color: #856404; } .error { background-color: #f8d7da; color: #721c24; } </style>这个案例展示了如何将Mirage Flow的能力,从一个通用的聊天框,转化为解决特定业务问题(商品推荐)的功能模块。通过精心设计提示词(Prompt),我们可以引导AI输出非常结构化、有用的内容。
5. 总结
走完这一趟,你会发现把像Mirage Flow这样的AI大模型集成到Vue3前端项目里,并没有想象中那么复杂。核心思路就是“封装”和“状态管理”。用一个独立的Store处理所有网络请求和状态,让业务组件可以干干净净地专注于展示和交互。
我们做的电商推荐案例,只是一个起点。你可以举一反三,把这种模式用到内容摘要生成、智能客服对话、甚至是根据用户描述实时生成UI代码等更多场景。关键是理解如何与AI进行有效的“沟通”——即设计好的提示词,以及如何在前端优雅地处理异步的、可能出错的AI响应。
在实际项目中,你可能还需要考虑更多,比如请求频率限制、响应内容的审核、在移动端的体验优化等。但有了今天打下的这个基础,那些都是可以一步步叠加和完善的。不妨就从你手头的一个小功能开始,尝试给它加上一点“智能”,看看能碰撞出什么新火花。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。