1. 项目背景与核心功能
去年ChatGPT刚发布时,我就在想:能不能自己写个前端应用,把它的API封装成更符合日常使用习惯的界面?当时市面上已有的客户端要么功能臃肿,要么缺少关键特性。经过几个周末的折腾,最终用Vue3实现了一个极简但实用的ChatGPT前端,核心解决了三个痛点:
连续对话:官方网页版虽然能记住上下文,但每次刷新就清零。我这个版本会把对话历史存在本地,重启浏览器也能继续聊。
Markdown渲染:ChatGPT返回的代码块、表格等内容,在前端用marked.js实现语法高亮和格式美化,比纯文本友好得多。
轻量化设计:整个应用打包后不到200KB,没有冗余依赖,甚至能在10年前的电脑上流畅运行。
2. 技术选型与项目搭建
2.1 为什么选择Vue3
对比React和Svelte后选择Vue3的原因很实际:
- 组合式API更适合封装聊天逻辑(后文会详细展示useChat这个hook)
- 单文件组件让Markdown渲染和样式隔离更简单
- Vite的启动速度比Webpack快5倍以上,适合快速迭代
创建项目时推荐用以下命令:
npm create vite@latest chatgpt-web --template vue-ts2.2 关键依赖清单
{ "dependencies": { "vue": "^3.3.0", "marked": "^5.0.0", // Markdown解析 "highlight.js": "^11.0.0", // 代码高亮 "pinia": "^2.0.0" // 状态管理 } }注意:不要安装官方OpenAI库,直接用fetch调用API。第三方库会增加打包体积,而且我们只需要最基础的POST功能。
3. 核心功能实现详解
3.1 连续对话的本地存储方案
实现本地记忆的关键是合理设计数据结构。我的方案是:
interface Message { role: 'user' | 'assistant' content: string timestamp: number } const chatHistory = useLocalStorage<Message[]>('chat-history', [])在Pinia中封装存储逻辑:
export const useChatStore = defineStore('chat', { actions: { addMessage(message: Message) { // 限制最多存储50条消息 if (this.messages.length > 50) { this.messages.shift() } this.messages.push(message) } } })3.2 Markdown渲染的优化技巧
直接使用marked会有XSS风险,需要配置sanitizer:
import { marked } from 'marked' import DOMPurify from 'dompurify' marked.setOptions({ highlight: (code) => hljs.highlightAuto(code).value }) const safeMarkdown = (raw: string) => { return DOMPurify.sanitize(marked.parse(raw)) }样式优化建议:
/* 让代码块有更好的视觉区分 */ pre { background: #f8f8f8; border-radius: 6px; padding: 12px; overflow-x: auto; } /* 表格添加斑马纹 */ table tr:nth-child(even) { background: #f2f2f2; }3.3 API调用的防抖与错误处理
ChatGPT API有时响应较慢,需要做请求管理:
let controller: AbortController | null = null const fetchAIResponse = async (messages: Message[]) => { // 取消上一个未完成的请求 if (controller) controller.abort() controller = new AbortController() try { const response = await fetch('https://api.openai.com/v1/chat/completions', { signal: controller.signal, method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${import.meta.env.VITE_OPENAI_KEY}` }, body: JSON.stringify({ model: 'gpt-3.5-turbo', messages }) }) if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`) return await response.json() } finally { controller = null } }4. 实战中的性能优化
4.1 虚拟滚动优化长对话列表
当聊天记录超过50条时,使用vue-virtual-scroller提升性能:
<template> <RecycleScroller :items="messages" :item-size="80" key-field="timestamp" > <template #default="{ item }"> <div class="message"> <!-- 消息内容渲染 --> </div> </template> </RecycleScroller> </template>4.2 智能缓存策略
对常见问题答案进行本地缓存:
const cachedAnswers = new Map<string, string>() const getCachedAnswer = (question: string) => { const key = question.trim().toLowerCase() return cachedAnswers.get(key) }5. 常见问题与解决方案
5.1 如何处理API限流?
实测发现两个有效策略:
- 指数退避重试:首次失败后等待1秒,第二次等待2秒,第三次4秒...
- 请求队列:当同时触发多个请求时,用队列顺序处理
const requestQueue: (() => Promise<void>)[] = [] let isProcessing = false const processQueue = async () => { if (isProcessing || requestQueue.length === 0) return isProcessing = true const task = requestQueue.shift() await task?.() isProcessing = false processQueue() }5.2 移动端适配技巧
在手机上有两个关键调整:
- 输入框使用
position: fixed固定在底部 - 启用iOS的弹性滚动:
body { -webkit-overflow-scrolling: touch; }6. 项目部署与后续迭代
6.1 最简单的部署方案
用Vercel或Netlify可以免费部署:
- 在项目根目录创建
vercel.json
{ "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }] }- 连接Git仓库自动部署
6.2 值得添加的功能
- 对话标签管理:给不同对话打标签方便检索
- 预设Prompt模板:一键插入常用提问模板
- API用量统计:监控token消耗情况
这个项目的核心价值在于:用最精简的代码实现了ChatGPT最实用的功能。去掉所有花哨的UI,专注于提升对话体验。如果你也想自己定制AI助手,不妨从这个最小可行版本开始迭代。