【极简】用 Vue 写一个 ChatGPT 前端应用,支持连续对话、Markdown 渲染与本地记忆

苦于网上很多 GPT 应用收费偏高,我就申请了自己的接口。

最近刚好在学 Vue,于是顺手写了一个极简版的 ChatGPT 前端页面。

目前已经实现了 连续对话、模型切换、Markdown 渲染、本地记录保存 等基础功能。

页面比较简单,布局也还有优化空间,不过很适合作为一个二开基础版本。


一、效果图

效果如下:


二、项目实现了什么?

这个小项目目前主要做了以下几个功能:

  • 支持基础聊天对话
  • 支持连续对话记忆
  • 支持 GPT-3.5 / GPT-4 模型切换
  • 支持简单的权限验证,防止误用 GPT-4
  • 支持 Markdown 内容渲染
  • 支持本地存储聊天记录
  • 支持"模拟打字机效果"

整体思路不复杂,适合 Vue 初学者练手,也方便后续继续扩展。


三、连续对话记忆是怎么实现的?

1. 基本思路

连续对话的核心其实很简单:

把用户之前提问过的内容暂时保存起来,每次发送新问题时,把最近几条历史问题一起发给接口,作为上下文参考。

为了节省 token 和调用成本,我这里没有把完整历史全量发送,而是采用了 数组切片 的方式,只保留最近几条记录。

2. 处理方式

我这里只提取了最近几条 用户输入,然后拼接成一段上下文提示词,再加上当前新问题一起发送给 GPT。

提示语如下:

以上是我之前问你的问题,只供你回答我接下来问题做参考,请你不要重复回答我之前问过的问题,只对新问题进行处理。新问题是:

这样做虽然比较"朴素",但实际效果还不错,已经能满足基本的连续对话需求。

3. 关键代码

javascript 复制代码
// 在发送消息时,将最近的对话历史一并发送
const maxHistoryLength = 4; // 最多保留最近 4 条记录
const trimmedChatHistory = this.chatHistory.slice(-maxHistoryLength);

// 提取用户消息
const userMessages = trimmedChatHistory
  .filter(item => item.sender === 'user')
  .map(item => item.message);

// 将用户历史问题拼接为字符串
const userMessageString = userMessages.join(' ');

// 拼接上下文提示词 + 当前问题
const fullUserMessage =
  userMessageString +
  ' 以上是我之前问你的问题,只供你回答我接下来问题做参考,请你不要重复回答我之前问过的问题,只对新问题进行处理。新问题是:' +
  this.userInput;

// 发起请求
const response = await this.chatGPTRequest(fullUserMessage);
this.handleBackendResponse(response);

4. 一个小优化

我还加了一个长度限制:

当上下文长度超过一定范围时,自动清空历史记录,避免内容越来越长导致费用增加或响应变慢。

javascript 复制代码
if (userMessageString.length >= 1200) {
  this.chatHistory = [];
  localStorage.removeItem('chatHistory');
}

四、为了防止误用 GPT-4,我加了一个简单权限验证

因为 GPT-4 的成本更高,有时候自己用着用着不小心切过去,钱包就开始难受了。

所以我做了一个非常简单的密码验证逻辑:只有输入正确密码,才能使用 GPT-4。

当然,这种前端校验只适合演示或自用。

如果你要真正上线,建议把校验逻辑放到后端去做,前端明文判断并不安全。

关键代码

javascript 复制代码
submitPassword() {
  if (this.password === 'useit') {
    ElNotification({
      title: 'Success',
      message: '权限已经获得!',
      type: 'success',
    })
    this.selectedModel = 'gpt-4'
    this.accessGranted = true
    this.showPasswordCard = false
  } else {
    ElNotification({
      title: 'Warning',
      message: '都是贫困惹的祸!',
      type: 'warning',
    })
    this.selectedModel = 'gpt-3.5-turbo'
  }
}

在提交问题前,也会再做一次判断:

javascript 复制代码
if (this.selectedModel === 'gpt-4' && !this.accessGranted) {
  ElNotification({
    title: 'Warning',
    message: '都是贫困惹的祸!',
    type: 'success',
  })
  return;
}

五、模拟打字效果

最开始为了提升一点"像聊天机器人"的感觉,我自己写了个模拟打字效果,让回复内容一个字一个字显示出来。

实现方式

拿到完整回复后,循环截取字符串逐步渲染:

javascript 复制代码
const content = response.choices[0].message.content;

if (content) {
  for (let i = 0; i < content.length; i++) {
    this.messages = [
      ...this.messages.slice(0, -1),
      { content: content.slice(0, i + 1), isUser: false }
    ];

    await this.$nextTick(() => {
      const messageContainer = this.$refs.messageContainer;
      messageContainer.scrollTop = messageContainer.scrollHeight;
    });
  }
}

不过后来我发现,如果直接用 流式输出(stream) ,本身就能达到类似的逐字效果,所以这个功能严格来说并不是必须的。

但既然都写出来了,也算是一个练手过程。


六、Markdown 渲染

GPT 返回的内容里,经常会有代码块、列表、标题等 Markdown 格式。

如果直接用普通文本显示,阅读体验会差很多,所以这里引入了 markdown-it 来做渲染。

安装依赖

bash 复制代码
npm install markdown-it

使用方式

javascript 复制代码
import MarkdownIt from 'markdown-it';
const md = new MarkdownIt();

parseMarkdown(text) {
  return md.render(text);
}

模板中通过 v-html 输出:

html 复制代码
<div v-else-if="message.content" class="text-message" v-html="parseMarkdown(message.content)"></div>

这样返回的 Markdown 内容就能正常显示为 HTML 结构,像代码块、标题、列表这些都更清晰。


七、本地存储聊天记录

为了让页面刷新后还能保留聊天记录,我用了 localStorage 做本地存储。

这部分和"上下文记忆"功能结合在一起后,体验会更像一个真正可持续聊天的应用。

保存记录

javascript 复制代码
handleBackendResponse(response) {
  this.chatHistory.push({
    sender: 'bot',
    message: response.message
  });

  this.saveChatHistoryToLocalStorage();
},

saveChatHistoryToLocalStorage() {
  localStorage.setItem('chatHistory', JSON.stringify(this.chatHistory));
}

加载记录

javascript 复制代码
loadChatHistoryFromLocalStorage() {
  const storedChatHistory = localStorage.getItem('chatHistory');
  if (storedChatHistory) {
    this.chatHistory = JSON.parse(storedChatHistory);
  }
}

清空记录

javascript 复制代码
clear() {
  this.userInput = '';
  this.chatHistory = [];
  this.messages = [];
  localStorage.removeItem('chatHistory');

  ElNotification({
    title: 'Success',
    message: '记录已经清空!',
    type: 'success',
  });
}

八、核心请求代码

这里通过 axios 向 OpenAI 接口发起请求,模型可以根据下拉框选择不同版本。

javascript 复制代码
chatGPTRequest(msg) {
  return new Promise((resolve, reject) => {
    axios({
      method: 'post',
      url: 'https://api.openai.com/v1/chat/completions',
      data: {
        max_tokens: 1200,
        model: this.selectedModel,
        temperature: 0.8,
        top_p: 1,
        presence_penalty: 1,
        messages: [
          {
            role: 'system',
            content: 'You are ChatGPT'
          },
          {
            role: 'user',
            content: msg
          }
        ],
        stream: false
      },
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer 你的key'
      }
    })
      .then(response => {
        resolve(response.data);
      })
      .catch(error => reject(error));
  });
}

九、页面结构

页面部分我用的是 Element Plus,整体布局比较简单,主要由以下几个模块组成:

  • 顶部模型选择
  • 左侧功能按钮
  • 中间消息展示区
  • 底部输入框与发送按钮

目前布局还比较朴素,主要先把功能跑通。

如果后续要继续优化,可以考虑:

  • 调整消息卡片宽度与留白
  • 优化移动端适配
  • 增加暗黑模式
  • 增加"新建会话 / 历史会话切换"
  • 支持流式输出
  • 支持代码高亮
  • 支持后端代理,隐藏 API Key

十、开发过程中几个值得注意的问题

1. 不建议把 API Key 直接写在前端

虽然这样调试方便,但如果你部署到线上,前端代码是可以被看到的,密钥很容易泄露。

更合理的做法是:

  • 前端请求你自己的后端
  • 后端再去请求 OpenAI 接口
  • 由后端统一做鉴权、限流、日志和模型控制

2. 连续对话最好使用标准 messages 结构

我目前这个版本是把历史问题拼成一段文本发给 GPT,比较简单直接。

如果想要更规范,建议使用 OpenAI 官方推荐的 messages 数组格式,把 userassistant 的历史消息一起传入,这样上下文质量会更好。

3. 模拟打字效果不如流式输出自然

手动逐字渲染能实现视觉效果,但严格来说不是真正的流式响应。

如果想体验更真实的"边生成边显示",建议后续接入 stream: true


十一、总结

这个 Vue 版 ChatGPT 前端应用,整体来说算是一个比较轻量的小练手项目。

虽然页面不算精致、实现方式也不算特别复杂,但基础功能已经具备:

  • 连续对话
  • Markdown 渲染
  • 本地记录存储
  • 模型选择
  • 简单权限控制

对于刚接触 Vue 或者想自己搭一个简单 AI 对话页面的同学来说,还是挺适合参考和二开的。

如果后续有时间,我准备继续补这些功能:

  • 流式输出
  • 多轮会话管理
  • 代码高亮
  • 后端代理接口
  • 更美观的聊天布局
  • 移动端适配

十二、完整代码

完整代码我就不在正文里逐段展开分析了,有兴趣的朋友可以自己继续研究、优化和二开。

如果大家有更好的实现思路,也欢迎评论区交流,一起进步。


十三、结尾

这个项目本质上就是一个 "能用、简单、方便继续改" 的版本。

如果你也是一边学 Vue 一边折腾 AI 应用,希望这篇文章能给你一点思路。

如果这篇文章对你有帮助,欢迎点赞、收藏、评论支持一下。

也欢迎大佬们指点优化方案,互相交流学习。谢谢!


相关推荐
大家的林语冰2 小时前
《前端周刊》尤大官宣 Vite 8 稳定版首发!npm 新官网?React 官网更新。focusgroup 新功能!
前端·javascript·vite
balmtv2 小时前
从“知识检索”到“深度推理”:Gemini 3.1如何用三层思考模式解决学术难题
人工智能·gpt·chatgpt
kuuailetianzi2 小时前
构建企业级督办任务系统:Vue3 + TypeScript 实战(多级任务拆解+批量操作+进度追踪+单元格合并)
前端·javascript·typescript
Hilaku2 小时前
王自如公开招聘全栈前端,要求有多离谱?
前端·javascript·ai编程
大漠_w3cpluscom2 小时前
CSS 技巧:CSS 中选择 html 元素的各种奇技淫巧
前端·css·weui
pengles2 小时前
基于RuoYi-Vue-Plus项目实现移动端项目
java·vue.js·uni-app
吴声子夜歌3 小时前
JavaScript——异步编程
开发语言·前端·javascript
陈随易3 小时前
农村程序员聊五险一金
前端·后端·程序员
恋猫de小郭3 小时前
Swift 6.3 正式发布支持 Android ,它能在跨平台发挥什么优势?
android·前端·flutter