1. 项目背景与核心价值
在信息爆炸的时代,智能问答系统正从简单的文本交互向沉浸式体验演进。我们团队最近完成了一个全平台适配的AI问答助手项目,核心目标是通过Vue+UniApp技术栈实现以下突破:
- 跨端一致性:一套代码同时运行在微信小程序、H5和App端
- 内容呈现:完美支持Markdown渲染(含数学公式)
- 交互体验:实现多模态输入输出(文本+语音+图像)
- 性能优化:在低端设备上保持流畅的对话体验
这个项目的独特之处在于,我们不仅实现了基础问答功能,还通过一系列创新设计让对话过程更具沉浸感。比如当AI回复包含代码片段时,会自动启用语法高亮;当涉及数学推导时,LaTeX公式会以SVG形式优雅呈现。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型与架构设计
2.1 为什么选择Vue+UniApp组合
经过多轮技术对比,我们最终确定的技术方案基于以下考虑:
-
开发效率:
- Vue的单文件组件(SFC)模式非常适合复杂UI开发
- UniApp的跨端能力可节省70%以上的重复工作量
-
生态支持:
- Vue生态有丰富的Markdown处理方案(如marked.js)
- UniApp插件市场提供现成的多端适配解决方案
-
性能表现:
- 实测数据显示:Vue3+Composition API比Options API节省约15%的内存占用
- UniApp的渲染层优化使H5首屏加载时间控制在1.2秒内
2.2 整体架构示意图
code复制[前端层]
├── Vue3 + Composition API
├── UniApp跨端引擎
├── Vant UI组件库(多端适配版)
[渲染层]
├── Marked.js + Katex(Markdown解析)
├── Prism.js(代码高亮)
├── Canvas渲染(公式/图表)
[服务层]
├── WebSocket长连接
├── 消息队列处理
├── 多模态处理器
提示:实际开发中我们发现,UniApp的easycom组件自动导入功能可以大幅减少import语句,建议在pages.json中配置:
json复制"easycom": { "autoscan": true, "custom": { "^u-(.*)": "@ui/components/u-$1/u-$1.vue" } }
3. Markdown与公式渲染实现
3.1 深度定制Markdown解析器
常规的Markdown渲染在移动端会遇到诸多问题:
javascript复制// 初始化marked解析器
import { marked } from 'marked';
import hljs from 'highlight.js';
marked.setOptions({
breaks: true,
highlight: (code, lang) => {
const validLang = hljs.getLanguage(lang) ? lang : 'plaintext';
return hljs.highlight(code, { language: validLang }).value;
},
// 自定义渲染规则
renderer: new marked.Renderer({
link(href, title, text) {
return `<a href="${href}" target="_blank" rel="noopener">${text}</a>`;
}
})
});
遇到的典型问题及解决方案:
-
表格溢出:通过CSS强制横向滚动
css复制.markdown-table { display: block; overflow-x: auto; white-space: nowrap; } -
代码块兼容性:统一使用Prism.js替代highlight.js
bash复制
npm install prismjs @dense-labs/prism-vue -
图片自适应:监听容器宽度动态设置max-width
3.2 数学公式渲染方案对比
我们测试了三种主流方案:
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| MathJax | 渲染质量高 | 体积大(300KB+) | PC端复杂公式 |
| KaTeX | 轻量(60KB) | 不支持全部LaTeX语法 | 移动端基础公式 |
| 服务端转SVG | 客户端零负担 | 需要网络请求 | 内容静态化场景 |
最终选择KaTeX+客户端缓存的混合方案:
javascript复制import katex from 'katex';
function renderFormula(formula, isInline) {
try {
return katex.renderToString(formula, {
throwOnError: false,
displayMode: !isInline,
macros: {
"\\RR": "\\mathbb{R}"
}
});
} catch (e) {
return `<span class="formula-error">${formula}</span>`;
}
}
4. 多模态交互实现细节
4.1 语音输入输出方案
语音模块架构设计:
code复制[录音阶段]
1. UniApp的getRecorderManager API
2. 采样率设置为16000Hz(平衡质量与体积)
3. 分片上传(每2秒一个chunk)
[处理阶段]
1. WebSocket实时传输音频二进制
2. 服务端转写文本+情感分析
3. 返回结构化结果:
{
text: "请问如何...",
emotion: "neutral",
suggest: ["相关问题1", "相关问题2"]
}
[播放阶段]
1. 使用innerAudioContext
2. 预加载机制(提前缓存3条回复音频)
3. 中断处理(用户说话时自动暂停播放)
4.2 图片交互的坑与解决方案
在实现图片问答时遇到的主要挑战:
-
上传限制:
- 微信小程序单张图片不能超过10MB
- 解决方案:客户端压缩+分片上传
javascript复制uni.compressImage({ src: filePath, quality: 70, success: res => { this.uploadChunks(res.tempFilePath); } }); -
跨端渲染差异:
- H5端可以使用标准的
标签
- 小程序需使用image组件
- 统一封装组件:
vue复制<template> <image v-if="isMp" :src="src" mode="aspectFit" /> <img v-else :src="src" style="max-width: 100%" /> </template> - H5端可以使用标准的
-
内容安全:
- 所有图片经过阿里云内容安全API过滤
- 敏感图片自动替换为预设占位图
5. 性能优化实战记录
5.1 首屏加载优化三部曲
-
代码分割:
javascript复制// vite.config.js build: { rollupOptions: { output: { manualChunks: { katex: ['katex'], markdown: ['marked', 'prismjs'] } } } } -
关键资源预加载:
html复制<link rel="preload" href="/static/katex.min.css" as="style"> -
接口缓存策略:
javascript复制const cache = new Map(); async function queryAPI(params) { const key = JSON.stringify(params); if (cache.has(key)) { return Promise.resolve(cache.get(key)); } const res = await axios.post('/api', params); cache.set(key, res.data); return res.data; }
5.2 内存管理技巧
在低端Android设备上发现的内存问题:
-
长列表渲染:
- 错误做法:直接v-for渲染全部消息记录
- 正确方案:使用虚拟滚动
vue复制<virtual-list :size="80" :remain="10"> <div v-for="item in list" :key="item.id"> {{ item.content }} </div> </virtual-list> -
图片缓存:
- 超过20张历史图片自动LRU清除
- 使用uni.saveFile持久化重要图片
-
WebSocket心跳:
javascript复制setInterval(() => { this.ws.send(JSON.stringify({type: 'ping'})); }, 30000);
6. 特色功能实现
6.1 上下文感知输入框
根据对话内容动态调整输入组件:
vue复制<template>
<component :is="currentInputType" @submit="handleSubmit" />
</template>
<script>
export default {
computed: {
currentInputType() {
if (this.lastMessage?.containsFormula) {
return 'formula-input';
} else if (this.lastMessage?.suggestVoice) {
return 'voice-input';
}
return 'text-input';
}
}
}
</script>
6.2 智能推荐算法
基于TF-IDF的简单实现:
javascript复制function getSuggestions(currentText, history) {
const terms = currentText.split(/\s+/);
const scores = {};
history.forEach(item => {
let score = 0;
terms.forEach(term => {
if (item.text.includes(term)) {
score += 1 / Math.log(1 + item.text.length);
}
});
scores[item.id] = score;
});
return Object.entries(scores)
.sort((a, b) => b[1] - a[1])
.slice(0, 3)
.map(([id]) => history.find(x => x.id === id));
}
7. 多端适配经验
7.1 平台特异性代码处理
推荐的条件编译方式:
javascript复制// #ifdef MP-WEIXIN
console.log('微信小程序特有逻辑');
// #endif
// #ifdef H5
import h5SpecificModule from './h5-only';
// #endif
7.2 样式适配方案
使用UNIAPP特有的rpx单位:
css复制.container {
/* 750rpx = 100%屏幕宽度 */
width: 750rpx;
padding: 20rpx;
}
/* 针对iOS的额外调整 */
/* #ifdef IOS */
.button {
padding-bottom: 10rpx;
}
/* #endif */
8. 调试与测试策略
8.1 真机调试技巧
-
Android远程调试:
bash复制
adb forward tcp:8080 tcp:8080 chrome://inspect/#devices -
iOS控制台日志:
javascript复制uni.onMemoryWarning(() => { console.warn('内存告警!当前页面:', getCurrentPages()); });
8.2 自动化测试方案
基于jest的测试配置示例:
javascript复制module.exports = {
preset: '@vue/cli-plugin-unit-jest',
transform: {
'^.+\\.vue$': 'vue-jest',
'^.+\\.js$': 'babel-jest'
},
testMatch: [
'**/__tests__/**/*.spec.js'
],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1'
}
}
9. 项目部署实战
9.1 微信小程序发布流程
-
修改manifest.json:
json复制"mp-weixin": { "appid": "wx123456789", "setting": { "urlCheck": false, "es6": true, "postcss": true }, "usingComponents": true } -
构建命令:
bash复制
npm run build:mp-weixin -
使用微信开发者工具上传
9.2 H5端Nginx配置
关键配置项:
code复制location / {
try_files $uri $uri/ /index.html;
gzip on;
gzip_types text/plain text/css application/json application/javascript;
# 解决History模式404问题
if (!-e $request_filename) {
rewrite ^(.*)$ /index.html last;
}
}
10. 项目演进方向
-
模型量化:将部分AI推理逻辑移到前端
- 使用TensorFlow.js实现轻量级模型
- 典型场景:情感分析、简单分类
-
离线能力:
- Service Worker缓存关键资源
- IndexedDB存储历史对话
-
AR融合:
- 通过WebXR实现增强现实问答
- 场景示例:设备维修指导
这个项目给我们最大的启示是:跨端开发不是简单的"一次编写,到处运行",而是需要深入理解各平台特性,在统一体验与平台适配间找到平衡点。特别是在处理富媒体内容时,要建立完善的降级方案,确保在任何环境下都能提供可用的基础体验。
