恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

跨端AI问答助手实战:Vue3+UniApp从零到上架全指南

  • 首页
  • 资讯中心
  • /
  • 跨端AI问答助手实战:Vue3+UniApp从零到上架全指南

相关资讯

Kronos-Tokenizer-2k 分词器排障指南:从依赖冲突到上下文长度限制一次搞定 2026/9/9 14:34:03
旧Mac装最新macOS:OCLP四步完整指南 2026/9/9 14:29:03
微信小程序AI实战:从架构设计到模型接入的完整教程 2026/9/9 14:29:03

最新资讯

Unity UGUI特效方案:UIEffect组件化实践与性能优化
Next.js + LangChain.js:前端工程师构建AI Agent的工程化路径
Taro+React中input光标跳动的根因与修复方案
ponytail:零配置静态开发服务器,专治SPA路由fallback与环境变量注入
三菱PLC五大功能指令详解:SUM、BON、DECO、ENCO、ZRST实战指南
React Email 邮件发送实战指南:用 Resend、Nodemailer 与 SendGrid 交付你的 React 邮件模板

今日推荐

基于MongoDB的图书管理系统:数据建模与Spring Boot+Vue实战
Claude Code安装配置全攻略:从零开始用上终端AI编程助手
tmux 会话管理与终端复用:AI 编程工作流的调度中枢实战

本周热门

超人会飞不算本事:系统稳定依赖清晰规则与边界设计
超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论
基于CNN的调制信号识别:MATLAB实现时频图分类实战

本月精选

自研推理加速器Redwood:两周内实现PyTorch模型高效部署的实战教程
V4L2摄像头采集实战:从camera_client.rar到出图全流程解析
从“谁发明了钢琴键”到知识问答智能体:RAG与记忆工程实践

跨端AI问答助手实战:Vue3+UniApp从零到上架全指南

发布时间:2026/9/9 14:34:03
跨端AI问答助手实战:Vue3+UniApp从零到上架全指南 1. 项目定位与技术选型为什么是 Vue 3 UniApp1.1 立项背景与核心需求拆解做这个 AI 问答助手最初的起因特别朴素我自己每天要在手机浏览器、微信小程序和桌面端来回切换向同一个大模型问问题复制粘贴好几遍会话记录还各存各的特别割裂。所以当时立项的思路很直接——能不能用一套代码把 AI 问答能力同时覆盖 H5、小程序和 App 三个端让用户在任何场景下都能打开就用会话还能同步。需求拆解下来其实就四件事。第一聊天界面本身要顺滑消息列表、输入框、流式输出、消息状态发送中、已完成、失败重试这些基础体验必须扎实。第二大模型返回的内容绝大多数是 Markdown代码块、表格、列表、数学公式都要能正确渲染这是问答质量的关键。第三交互不能只停留在文字现代 AI 应用基本都支持图片识别和语音输入多模态交互必须纳入第一版。第四既然是“沉浸式”整个界面从配色、动效、输入反馈到加载状态都要围绕“让用户专注于对话本身”来设计不能出现那种临时拼凑的割裂感。想清楚这件事之后我用一个周末画了原型和功能清单又用三天做了技术验证最终确定了 Vue 3 UniApp 的方案。后面所有开发都围绕“一套代码、三端可用、重体验、可扩展”这四个原则推进。1.2 技术栈对比原生小程序 / React Native / Flutter / UniApp选型阶段我认真对比过四条路线这里直接把当时的评估结论摆出来。方案一套代码覆盖端数学习成本Markdown/富文本生态社区成熟度我的判断原生小程序仅微信小程序低组件多但仅限小程序内高无法覆盖 H5 和 App直接排除React NativeiOS Android中高需自建渲染链高H5 支持弱还得另写 Web 端Flutter全端高Dart 语法需大量自绘中高学习成本高团队不熟UniAppVue 3H5 小程序 App中Vue 语法即可mp-html、markdown-it 等成熟组件多高最贴合“快速验证 全端覆盖”的目标UniApp 最吸引我的一点是它对 Vue 语法的高度兼容团队里已经熟悉 Vue 的同学几乎不需要额外学习就能上手。同时它的条件编译机制可以针对 H5、小程序、App 写差异化代码比如流式请求在 H5 用 fetch 的 ReadableStream在小程序用 request 的 enableChunked这部分差异能被很好地隔离在封装层里。再加上 mp-html 这类跨端富文本渲染组件已经很成熟Markdown 转 HTML 之后再渲染的链路是一条被多少人验证过的路踩坑成本可控。当然 UniApp 也有自己的脾气尤其是 App 端的 webview 渲染和原生组件的层级问题后面在第 4 章我会专门展开讲我遇到的那些坑。1.3 功能清单与“沉浸式”体验目标最终功能清单定稿为流式对话、Markdown 渲染、数学公式行内和块级、代码高亮与复制、图片识别输入、语音输入、会话历史、上下文管理、分享、以及地图/视频等场景化扩展能力。“沉浸式”这个目标落地到具体体验指标上有三条硬性要求第一首屏速度要快冷启动后 1 秒内进入可对话状态第二流式输出的打字机效果要稳定在 30ms 一帧的节奏不能出现整段等待或频繁卡顿第三所有操作都要有即时反馈比如发送消息后气泡立即出现输入框高度随内容自适应卷起时不能遮挡内容。这三条指标看着简单实际落实的时候牵扯到渲染方案、请求封装、端差异适配、内存控制等一整套工程问题。下面我按工程推进的顺序把这套系统从初始化到上架的关键环节完整过一遍。2. 工程初始化与关键依赖选型2.1 创建项目与目录规划我用的是 Vue 3 Vite 版本的基础模板官方 CLI 一条命令就能拉下来npx degit dcloudio/uni-preset-vue#vite my-ai-assistant cd my-ai-assistant npm install npm run dev:mp-weixin注意这里一定要选#vite分支早期还有很多#vue3老模板依赖的还是 webpack跑起来慢而且后续生态兼容性差。项目初始化后我做的第一件事不是写代码而是把目录结构按模块划分清楚src/ ├── api/ # 接口请求层统一封装 AI 流式/普通请求 ├── components/ # 通用组件消息气泡、输入栏、图片预览等 ├── pages/ # 页面chat(主对话页)、history(会话列表)、setting ├── store/ # 状态管理Pinia ├── utils/ # 工具函数markdown解析、SSE解析、格式化 ├── static/ # 静态资源 └── styles/ # 全局样式与主题变量在 HBuilderX 里创建项目也可以但我个人更喜欢 CLI 方式因为可以用 VSCode 配 Volar 插件写代码类型提示和格式化体验比 HBuilderX 编辑器舒服太多。这个习惯从 Vue 2 时代一直保留到现在。2.2 Markdown 组件选型为什么选 mp-html这是整个项目里最关键的一个选型没有之一。AI 返回的内容是 Markdown 字符串要渲染成带样式、可复制、支持代码高亮的富文本在浏览器里方案很成熟marked highlight.js但到了小程序和 App 里情况完全不一样。小程序没有 DOM不能直接用 innerHTML官方提供的 rich-text 组件不支持自定义事件复制代码、点击链接这些交互全都做不了。App 端如果走 webview 渲染又会有和原生输入框层级冲突的问题。所以必须用一个跨端富文本渲染组件。我当时对比了三个方案towxml功能全支持 Latex 和流程图但体量大、更新慢对 Vue 3 新项目的适配要看运气。mp-html轻量、维护活跃、支持自定义标签和事件还能通过插件扩展 latex 能力是社区里用得最多的方案。自己解析自己画不现实Markdown 的边界情况太多一个小程序原生组件写出来至少要一周。最终选了 mp-html。它在 H5 端会退化成基于 HTML 的渲染在小程序和 App 端用自定义组件解析 nodes 数组渲染三端共用一套 API非常贴合我们的需求。安装也简单npm install mp-html然后在需要使用的地方引入import MpHtml from mp-html/dist/uni-app/components/mp-html/mp-html注册成组件后直接传入content属性即可。但传入前我们需要先把 Markdown 字符串转成 HTML这一步我用的仍然是 marked属于稳定可靠、注释多、任何搜索都能找到答案的经典方案。2.3 公式渲染与代码高亮的跨端方案公式渲染是 AI 问答里绕不开的需求尤其是数学、物理、金融这类领域。Markdown 里的公式一般分两种行内公式$...$和块级公式$$...$$。在 Web 端KaTeX 是首选快且稳。但在小程序端KaTeX 生成的 HTML 和 CSS 依赖 DOM直接套进 mp-html 会出现样式错乱。我的处理思路是在把 Markdown 转 HTML 之前先拦截公式部分用 KaTeX 在服务端思路下先渲染成带行内样式inline style的 HTML 片段再整体交给 mp-html。这样三端拿到的都是渲染完成的 HTML小程序端不再需要跑 KaTeX。import { marked } from marked import katex from katex import katex/dist/katex.min.css const escapeHtml (str) { return str.replace(//g, amp;).replace(//g, lt;).replace(//g, gt;) } // 先处理块级公式 $$...$$再处理行内公式 $...$ function renderFormula(markdownText) { let html markdownText.replace(/\$\$([\s\S]?)\$\$/g, (_, expr) { try { return katex.renderToString(escapeHtml(expr), { displayMode: true, throwOnError: false }) } catch (e) { return div classformula-error公式解析失败/div } }) html html.replace(/\$([^$\n]?)\$/g, (_, expr) { try { return katex.renderToString(escapeHtml(expr), { throwOnError: false }) } catch (e) { return $${expr}$ } }) return html }这里有一个必须注意的坑throwOnError: false一定要加因为 AI 生成的文本里经常出现不完整的公式比如少了一个$符号不加这个参数整个渲染就会被一个异常打断。另外处理顺序必须先块级后行内否则$$会被行内的正则先吃掉。代码高亮则是在 marked 的配置里接入 highlight.jsimport hljs from highlight.js marked.setOptions({ breaks: true, gfm: true, highlight(code, lang) { if (lang hljs.getLanguage(lang)) { return hljs.highlight(code, { language: lang }).value } return hljs.highlightAuto(code).value } })渲染完成后给代码块外层套一个带复制按钮的自定义标签mp-html 通过tag-style属性注入基础样式再监听linktap和自定义事件处理点击复制。这套链路我跑通后Markdown 相关需求就算完成了七成。3. 核心交互实战流式输出、Markdown 渲染与多模态3.1 流式输出打字机效果从零实现AI 问答如果不做流式输出用户体验会非常糟糕。发送一个问题后干等十几秒才收到完整回复用户大概率以为应用卡死了直接退出去。流式输出让文字像打字机一样逐字出现既是产品体验的需要也是大模型接口的标准能力。后端如果支持 SSEServer-Sent Events前端接收流式数据的方式在三个端不完全一样这里的条件编译是关键。H5 端用 fetch 的 ReadableStream// #ifdef H5 const response await fetch(apiUrl, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify(payload) }) const reader response.body.getReader() const decoder new TextDecoder() let buffer while (true) { const { done, value } await reader.read() if (done) break buffer decoder.decode(value, { stream: true }) // 按 SSE 格式解析 data: 开头的行 const lines buffer.split(\n) buffer lines.pop() // 最后一行可能不完整留到下次 for (const line of lines) { if (line.startsWith(data:)) { const data line.slice(5).trim() if (data [DONE]) return handleChunk(JSON.parse(data)) } } } // #endif小程序端没有 ReadableStream但uni.request在较新版本里支持enableChunked属性可以拿到分块数据// #ifdef MP-WEIXIN uni.request({ url: apiUrl, method: POST, data: payload, enableChunked: true, success(res) { // 注意分块模式下成功回调会随每个 chunk 触发 } }) // #endif这里要特别说明一个容易踩的坑小程序端的enableChunked模式下返回的数据并不保证是一行完整的 SSE 数据可能是半截中间要自己做缓冲拼接用和 H5 端一样的 buffer 策略处理。数据类型上真机测试时 chunk 有时是 ArrayBuffer有时直接是字符串需要判断一下再转码。封装完流式接收之后消息的中转状态也要一起处理。我的做法是消息对象进入消息列表时状态标记为streaming每收到一段内容就push到消息的 content 字段中然后触发视图更新。这里有个性能隐患——如果每个 chunk 都触发一次 Vue 重新渲染数据量大的时候页面会明显卡顿。解决办法是做一个 60ms 的节流攒够一小批内容再统一更新 DOM实测打字机效果依然连贯但渲染压力小了很多。3.2 Markdown 内容安全渲染与交互增强流式输出的过程中Markdown 内容也是不完整的。如果每收到一个 chunk 就重新执行 Markdown 转 HTML会带来两个问题一是性能开销大marked 解析一次 1000 字的文档大约要 10ms 左右高频执行会卡二是渲染闪烁未闭合的代码块在转 HTML 时会生成不完整的标签视觉上会跳。我的方案是“节流 最终重渲染”双轨制流式过程中每 300ms 做一次 Markdown 渲染让用户看到结构逐渐成型流结束之后对完整文本做一次最终渲染保证代码高亮和公式渲染的准确性。用lastRenderTime控制节奏流结束时强制刷新一次这个逻辑非常稳定。安全方面也要注意。AI 返回的内容虽然是模型生成的但很可能包含 HTML 标签或者用户上传文本里的恶意脚本。mp-html 默认会对 HTML 做过滤但为了保险我在转 HTML 之前先把script、iframe、onerror这类风险内容清洗一遍。简洁版白名单过滤函数如下function sanitizeHtml(html) { return html .replace(/script[\s\S]*?\/script/gi, ) .replace(/iframe[\s\S]*?\/iframe/gi, ) .replace(/\son\w\s*\s*[][^]*[]/gi, ) }交互增强方面我做了三件事。第一代码块右上角加复制按钮通过 mp-html 的自定义标签pre包一层copy-btn来实现。第二链接点击统一走linktap事件H5 端新窗口打开小程序端 copy 链接提示用户去浏览器访问。第三表格在窄屏下横向滚动避免撑破气泡。这些都是细节但“沉浸式”的体验很大程度就体现在这些细节上。3.3 多模态输入图片、语音与上下文管理多模态交互是这一版的重点也是对比同类竞品时的加分项。先说完输入侧的三条路文字、图片、语音。图片输入用 UniApp 的 API 封装选择图片后先压缩再上传uni.chooseImage({ count: 1, sizeType: [compressed], sourceType: [album, camera], success: (res) { const tempFilePath res.tempFilePaths[0] // 长图或大图先压缩 uni.compressImage({ src: tempFilePath, quality: 80, success: (compressRes) { uploadImage(compressRes.tempFilePath) } }) } })语音输入这块我一开始想接各家平台的语音识别 SDK后来发现要把离线唤醒、权限申请、录音格式转换全部搞定工作量大且每个平台都要单独适配。第一版我先用了「语音转文字后发送」的轻方案按住说话利用系统录音生成音频文件上传到后端 ASR 服务转成文本再走正常的文本发送链路。虽然多一步上传但胜在稳定而且用户感知到的仍然是“我说话AI 回复”。上下文管理是 AI 问答助手里的隐形核心。大模型的上下文窗口有限不能把全部历史都塞进去。我的策略是做三层保留最近 10 轮完整消息作为主上下文对更早的消息做摘要压缩成一段“总结性记忆”放在最前面超出窗口后按滑动窗口丢弃。这个逻辑放在后端处理前端只需要维护一个sessionId和消息时间线。// 发送消息时的消息体结构 { sessionId: xxx, content: 问题内容, type: text | image | voice, images: [url1, url2], // 图片模式下附带 context: { recent: [...最近10轮], summary: 对更早内容的摘要 } }3.4 会话历史与消息模型设计会话历史看起来简单做起来有很多讲究。我使用的消息模型长这样{ id: msg_001, role: user | assistant, type: text | image | voice | system, content: 完整内容, status: sending | streaming | done | failed, timestamp: 1700000000000, extra: { images: [], formulaRendered: false, // 是否已完成公式最终渲染 rawContent: // 未渲染的原始 markdown } }会话列表页和对话页通过 Pinia 共享状态。切换会话时从本地存储uni.setStorageSync读取历史消息进入对话页时一次性渲染。这里有一个体验优化点长会话进入页面时如果一次性渲染几十条消息页面会卡所以只渲染最近 30 条向上滚动到底部时再加载更早的消息。列表用虚拟滚动思路处理每条消息组件用v-memo缓存避免重复渲染。4. 全端适配细节与高频问题排查4.1 三端差异对照H5 / 微信小程序 / App一套代码写完后真正的战斗才刚开始——三端适配。这里有一个残酷的现实UniApp 所谓的“一套代码多端运行”指的是业务逻辑可以复用但每个端的坑都得单独踩一遍。我整理了开发中遇到的差异对照功能点H5微信小程序App流式请求fetch ReadableStream 稳定enableChunked 可用但数据可能分包需要原生插件或 webview 桥接键盘避让浏览器自动处理需adjust-position 手动计算softinputMode配置地图能力直接用腾迅地图 JS SDK需 map 组件 key 配置原生模块或 webview分享浏览器原生分享onShareAppMessage 定义后才有右上角菜单需原生分享插件视频播放video 标签video 组件需处理同层渲染原生播放器格式支持有限路由参数正常 queryoptions 传参注意类型会被强转正常 query其中最大的坑在小程序的视频和地图组件。比如 vue 播放 m3u8 在 H5 上是video标签直接搞定到了小程序端iOS 支持 m3u8 但 Android 部分机型播放不了到了 App 端又得换原生播放器方案。最后我的处理是做一个统一的视频组件内部用条件编译分别对接三端的播放能力并约定后端转码时同时提供 mp4 和 m3u8 两种格式前端按端选择。这也是“一套业务逻辑多端差异化实现”的标准范式。4.2 软键盘遮挡、路由参数与分享覆盖问题实录软键盘遮挡这个小程序高频问题在 AI 对话场景里尤其要命。用户正在打字键盘弹起来把输入框顶上去刚好把最后一条 AI 回复挡住。adjust-position属性确实能顶起页面但 iOS 上有时会失灵或者顶起的高度不对。我最终的解决方案是组合拳// page.json 中对应页面配置 { path: pages/chat/chat, style: { app-plus: { softinputMode: adjustResize } } }小程序端则在输入框聚焦时手动计算uni.getSystemInfoSync().windowHeight与uni.createSelectorQuery()获取输入框位置做一个兜底滚动// 键盘弹起时确保最后一条消息可见 const query uni.createSelectorQuery().in(instance) query.select(.message-list).boundingClientRect() query.select(.input-bar).boundingClientRect() query.exec((res) { if (!res || !res[0] || !res[1]) return const threshold res[1].top - res[0].top if (threshold 200) { // 滚动到底部 uni.pageScrollTo({ scrollTop: 999999, duration: 200 }) } })路由参数这个看似基础的问题实际开发中也常被问。UniApp 中uni.navigateTo传参页面里用onLoad(options)接收但要注意一个细节参数值会被 URI 编码如果参数里有特殊字符如 JSON 字符串、中文必须decodeURIComponent处理uni.navigateTo({ url: /pages/chat/chat?sessionId${encodeURIComponent(sessionId)} }) // 接收页 onLoad(options) { const sessionId decodeURIComponent(options.sessionId || ) }分享覆盖问题也是热搜里的高频问题。很多项目会在 App.vue 里通过uni.onShareAppMessage定义全局分享但这样会导致页面级自定义分享失效。正确的做法是页面级onShareAppMessage优先级高于全局如果全局方法把页面方法覆盖了检查一下是不是在mixin里重复定义了相同钩子或者分享按钮绑定的事件名写错了。我最终的做法是全局只设默认分享文案每个页面按需重写// 对话页自定义分享 onShareAppMessage() { return { title: AI问答${this.currentQuestion.slice(0, 20)}, path: /pages/chat/chat?sessionId${this.sessionId}sourceshare } }4.3 场景化扩展地图导航与视频播放入口AI 问答助手里最常被问到的一类问题是“帮我找附近的地铁站”或者“给朋友发个定位”。如果 AI 只能给文本回答体验就断了。所以我在消息里定义了一种特殊类型action当模型识别到用户意图需要地图或导航时返回结构化指令前端渲染成可点击的卡片。地图这块我用的是腾讯地图因为 UniApp 官方对腾讯地图的兼容性最好。关键配置在小程序端的manifest.json里{ mp-weixin: { appid: 你的小程序appid, permission: { scope.userLocation: { desc: 用于获取您的位置信息以提供附近推荐 } }, requiredPrivateInfos: [getLocation] } }导航调用时先uni.getLocation拿到当前坐标再打开地图 app 或导航uni.openLocation({ latitude: destLat, longitude: destLng, name: 目的地名称, address: 详细地址, fail: () { // 降级复制坐标给用户 uni.setClipboardData({ data: ${destLat},${destLng} }) } })视频播放同样做成卡片形式。AI 回答中如果包含视频资源链接我会解析成video-card类型呈现播放组件内部按端处理 m3u8 和 mp4 的格式兼容问题。这样用户不用跳出应用就能看完整的多媒体内容“沉浸式”闭环才算真正成立。5. 打包、上架与隐私合规5.1 微信小程序打包与开发者工具联调小程序是整个项目最先发布的端因为审核流程相对短可以快速验证产品。开发过程中最常遇到的一个诡异现象就是“运行到微信开发者工具上没反应”命令行npm run dev:mp-weixin执行完开发者工具没有任何反应。这个问题排查了很久最终定位到两个原因一是开发者工具的“服务端口”没打开需要在设置-安全设置里开启“服务端口”二是项目路径里有中文或特殊字符微信开发者工具的编译进程无法正确读取。另一个原因是 CLI 创建的项目没有生成project.config.json中的miniprogramRoot字段工具找不到编译产物。解决方式是在项目根目录补上{ miniprogramRoot: dist/dev/mp-weixin/, projectname: ai-assistant, setting: { urlCheck: false } }urlCheck这个配置特别重要开发阶段如果没关闭所有非 https 的请求都会被拦截控制台会报“不在以下 request 合法域名列表中”。当然上线前必须把后端域名配置到小程序后台的 request 合法域名里并开启 HTTPS。小程序分包也是必做的。因为引入了 mp-html、highlight.js、katex 这些库主包体积很容易超过 2MB 限制。我把对话页放在主包把历史记录、设置、帮助中心等低频页面拆到分包同时把静态图片资源尽量走 CDN最终主包控制在 1.4MB 左右。分包配置在pages.json里{ pages: [pages/chat/chat, pages/index/index], subPackages: [ { root: pages/history, pages: [history-list, session-detail] }, { root: pages/setting, pages: [about, privacy, feedback] } ] }5.2 App 打包与安卓应用市场上架App 端我走的 HBuilderX 云打包选择离线打包还是云打包取决于团队情况。云打包省事但每次都要传包、等待离线打包需要配置 Android Studio 工程适合需要深度集成原生模块的场景。第一版我用的是云打包把 Manifest.json 里的模块配置好地图、推送、分享等按需勾选打包成 apk 做测试。安卓应用市场上架这个环节比预想中麻烦。我整理了三个市场上的共性要求要求项说明我的处理软件著作权上架软著是硬门槛提前申请一般需要 1-2 个月注意预留时间APP 备案备案是基础准入在云厂商的备案系统提交大约 1-2 周隐私政策必须有独立可访问的隐私政策页面上线前部署到独立域名并做好弹窗加固与签名部分市场要求签名安全检测用市场提供的加固工具处理后再上传应用内用户协议首次启动弹窗展示见 5.3 的交互实现一个容易忽略的坑是不同市场的包名和签名要求可能不同如果在多个市场上架建议用同一个正式签名文件避免日后更新时被拒。签名文件一定要备份好丢了之后应用无法更新只能改名重新上架这个损失非常大。5.3 隐私政策与用户协议的交互处理iOS App 审核对隐私合规尤其严格热搜里也提到“当用户不同意隐私政策及用户协议时退出 app”的实现问题。首次启动时必须弹窗展示隐私政策链接和用户协议链接用户点击“同意”后才能进入应用。这里有一个现实问题App 端在用户同意隐私政策之前不能采集任何个人信息包括设备信息这意味着很多统计 SDK 和推送 SDK 必须延迟初始化。我的处理方式是在 App.vue 的onLaunch里先判断是否已经同意未同意的只启动最基本的渲染框架同意后才动态引入其他 SDK。弹窗交互代码// App.vue onLaunch 中调用 checkPrivacyAgreement() { const agreed uni.getStorageSync(privacy_agreed) if (agreed) return uni.showModal({ title: 用户协议与隐私政策, content: 欢迎使用AI问答助手请先阅读并同意《用户协议》和《隐私政策》后再继续使用。, confirmText: 同意并继续, cancelText: 不同意, success: (res) { if (res.confirm) { uni.setStorageSync(privacy_agreed, true) // 这里再初始化统计、推送等 SDK } else { // 用户不同意按平台规则退出 // #ifdef APP-PLUS plus.runtime.quit() // #endif } } }) }有一个体验细节iOS 上应用不能直接调用系统级退出plus.runtime.quit()在 iOS 上行为不稳定。比较稳妥的降级方案是不同意的用户停留在协议确认页页面按钮全部置灰应用无法继续使用。安卓系统下plus.runtime.quit()可以正常退出这个平台差异要在代码里明确区分。由于 iOS 审核对“不同意即退出”的交互比较敏感我最终改成了“不同意则无法使用应用功能”的交互这样既合规也避免了被审核打回。隐私政策页面上线前一定仔细核对内容是否声明了收集哪些信息、用途是什么、是否有第三方 SDK 列表、用户如何注销账号、如何联系开发者。这些内容缺失或含糊审核基本都会被打回。建议把 SDK 列表做一张独立页面方便后续新增 SDK 时单独更新不用整个改隐私政策文档。6. 性能优化与个人心得6.1 长对话场景的内存与渲染优化AI 问答场景有一个天然的重负载对话越长DOM 节点越多。小程序端尤其明显几十条消息加上每条的代码块高亮 DOM页面能明显变得卡顿。我做了四层优化。第一层是分页渲染进入页面只渲染最近 30 条往上滑动态加载更多不让消息列表无限增长。第二层是消息组件缓存用v-memo根据消息 id 和 status 判断是否需要重新渲染已经处于done状态的旧消息完全不参与更新。第三层是流式期间的节流前面讲过60ms 合并一次内容更新。第四层是资源释放图片消息用懒加载离开会话页时清掉预览缓存。template view v-formsg in visibleMessages :keymsg.id v-memo[msg.id, msg.status, msg.content.length] message-bubble :msgmsg / /view /template内存监控上小程序开发者工具的性能面板和真机调试的 Memory 信息都值得盯一下。我遇到过连续跑 30 分钟长对话后内存持续上涨的情况最后定位到是流式拼接时字符串重复创建以及图片 base64 数据未释放。字符串拼接用数组push后join替代图片改用文件路径而非 base64 存储问题就解决了大半。6.2 开发者工具调试与发布流开发期间的调试效率直接决定项目进度。VSCode Volar 插件的组合是我比较推荐的Vue 3 的script setup语法会有完整的类型提示。注意安装完 Vue 3 项目依赖后如果 Volar 没生效检查是否为 VSCode 装了旧版 Vetur两个插件冲突会导致提示全部失效卸载 Vetur 只保留 Volar。调试接口时Chrome 的开发者工具在小程序端也能派上用场。运行到微信开发者工具后在“调试器”面板里可以看到小程序运行时的网络请求和控制台日志比在 H5 开发环境里看更接近真实环境。但要注意小程序的request域名校验和本地代理可能会拦截开发环境请求配置urlCheck: false只对开发环境生效生产环境域名必须在后台配置白名单。发布之前我习惯按清单过一遍三端分别跑冒烟测试登录、发消息、图片识别、断网重连、分享、地图、隐私弹窗验证、分包体积检查、内存泄漏检查、不同机型适配测试。这个清单虽然繁琐但能避免上线后被用户骂“这也能叫沉浸式”。6.3 根据真实反馈的迭代方向第一版上线后用户反馈最集中的几个点我记录下来作为第二版的迭代方向一是 Markdown 表格在手机端的横滑体验不够自然计划改成卡片式展示二是语音输入在嘈杂环境下识别率偏低计划接入更专业的 ASR 服务三是希望支持多模型切换比如通用对话、编程问答、数学解题各用一个模型这个已经在后端做好接口预留四是会话同步用户希望在手机端聊到一半到电脑上继续这需要后端加一层用户体系和云端会话存储。另外还有一个小技巧善用uni.setStorageSync做本地会话缓存即使网络断开历史记录也能查看。这在弱网场景下对留存率帮助很大。这个项目从立项到第一版上架前后三周踩过的坑比想象中多但跑通后的获得感也强。如果让我总结最值得分享的几点经验第一选型阶段把“跨端富文本渲染”这块调研清楚能避开后续 70% 的适配问题第二流式输出是 AI 问答的体验底线再难也要在第一版做出来第三隐私合规不是上线前的临时任务要从第一天就设计进去。希望这篇文章能把你在 AI 问答助手开发路上的弯路绕掉一部分。项目代码结构和核心实现都在上面了有具体问题欢迎在评论区交流。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号