uni-app在线考试练题小程序全解析:从题库设计到抓包调试

发布时间:2026/10/6 14:28:40
uni-app在线考试练题小程序全解析:从题库设计到抓包调试 做“在线考试练题小程序”这个项目说难不难说简单也不简单。难的不是写答题页面而是把题库、错题、考试计时、数据统计这些环节串起来还要保证在不同机型、不同微信版本下不出幺蛾子。今天这篇就把我手上这套 2026 升级版的完整开源工程讲透从需求拆解到数据模型、从搭建流程到抓包调试全部摊开聊顺便把源码里那些坑也一并交代清楚。先说清楚这套东西是什么。整个工程代号2048-小程序是一份完整的 uni-app 小程序源码配套服务端接口和题库导入脚本全部开源交付。它解决的典型问题是机构或老师手里有一堆练习题目想快速上线一个刷题小程序但又不想从零写前端、不想被第三方平台按年收费绑死。适合的人群很广包括培训机构的教学负责人、独立开发者接外包项目、甚至想自己搭一个内部考试系统用来做员工培训考核的团队。拿到源码后改个标题、导入自己的题库就能跑起来上线。下面直接进入正题。1. 在线练题小程序的核心设计与需求拆解1.1 先把需求说清楚练题不是“出题判断对错”这么简单很多人第一次做练题类小程序容易把功能想窄了以为做一个页面展示题目点选项给个对错提示就算完事。实际跑过一遍用户反馈就会发现真正影响留存和复购的是练习记录、错题归集、成绩统计和“刷题节奏感”。我这套 2026 升级版的核心功能清单如下章节练习按分类目录逐题刷适合系统学习和知识点巩固。随机练习从题库里随机抽题适合零散时间快速过知识点。模拟考试限定题量和考试时间倒计时结束后自动交卷按得分率出结果。错题本答错的题自动进入错题集可重复练习练完可手动移除或标记“已掌握”。收藏夹用户自行收藏拿不准的题目方便考前集中复习。统计面板展示总刷题数、正确率、连续打卡天数、各分类正确率。每日一练每天固定推送一小组题用打卡机制拉住用户活跃度。为什么 2026 版强调“升级”因为早期版本里错题本和练习记录都是纯本地缓存换设备或者清缓存就全丢了。这一版把用户做题记录同步到服务端同时在本地留一份快照双写兜底。这样既兼顾了弱网环境下的体验也解决了数据持久化问题。这里有一个很实际的选型取舍要讲题目数据到底放前端还是放服务端如果题目量小于 500 道且不需要频繁更新确实可以打包进小程序包里甚至做成 JSON 文件直接引用。优点是响应快、不依赖网络、服务器压力为零。但一旦题库超过 2000 道或者每周都要更新题目就必须走接口拉取。小程序主包体积限制是 2MB放几百道带图片的题目基本就把包撑爆了。所以这套源码采用的是“服务端拉取 本地缓存”方案首次进入按分类拉题列表做过一次的分类把题目数据缓存在本地 storage二次进入直接读缓存除非手动触发同步否则不会再请求接口。1.2 技术选型为什么我坚持用 uni-app 而不是微信原生技术选型往往是项目开始前最纠结的部分。用微信原生 WXML 开发上限更高但代码只能跑在微信小程序里。用 uni-app 则是一套 Vue 代码同时编译输出到微信小程序、支付宝小程序、H5 和 App。对于练题类这种以表单交互为主、页面结构并不复杂的项目uni-app 的跨端收益非常明显。从 2026 升级版的实际编码体验来看我的建议是如果你只做微信端且团队里有人熟 WXML可以选原生如果将来要考虑多端或者你本身更熟 Vue 语法那直接 uni-app 更省事。这套源码基于 uni-app 3.x Vue 3 编写用的是 Composition API。技术栈对比如下维度微信原生uni-app跨端能力仅微信微信/支付宝/H5/App开发语言WXML/WXSS/JSVue 2/3上手门槛需单独学语法会 Vue 基本无缝原生能力调用微信 API 直接调统一封装后条件编译项目适合度简单工具类题库、商城、内容类应用更合适这套源码的服务端部分也做了两手准备。默认提供一套基于 Node.js 的轻量 API方便本地开发调试同时保留了一个 Spring Boot MyBatis 的后端版本适合已经有 Java 技术栈的团队把项目接进现有后台系统。接口协议保持一致前端不需要因为换后端而改逻辑。还有一点必须提醒2048-小程序.zip是 uni-app 源码工程它不能像网页那样直接扔到浏览器里打开预览也不是一个打包好的小程序成品。你需要通过 HBuilderX 导入并编译到微信开发者工具里运行或者用命令行方式运行npm run dev:mp-weixin。这个细节我在后面实操部分会重点展开因为太多人在这一步卡住。2. 源码工程结构与数据模型拆解2.1 前端目录结构每一个文件是干什么的先贴一下源码工程的核心目录结构拿到的压缩包解压后你会看到这样的布局2048-小程序/ ├── pages/ │ ├── index/index # 首页展示分类、每日一练入口 │ ├── practice/practice # 练习页章节/随机模式共用 │ ├── exam/exam # 模拟考试页 │ ├── wrong/wrong # 错题本 │ ├── favorite/favorite # 收藏夹 │ ├── stats/stats # 统计面板 │ └── login/login # 登录授权页 ├── components/ │ ├── question-card/ # 题目卡片组件 │ ├── answer-options/ # 选项区域组件处理单选/多选 │ ├── progress-bar/ # 进度条组件 │ └── countdown-timer/ # 倒计时组件 ├── api/ │ ├── request.js # uni.request 统一封装 │ ├── question.js # 题目相关接口 │ ├── user.js # 用户相关接口 │ └── record.js # 做题记录与错题接口 ├── utils/ │ ├── answer.js # 答案比对算法 │ ├── storage.js # 本地缓存封装 │ └── format.js # 时间/数字格式化 ├── static/ │ └── images/ # 图标与占位图 └── manifest.json # uni-app 配置文件AppID 留空待替换这里常用的页面就是practice和exam两个页面共用question-card组件但渲染模式上有明显区别。练习模式没有强制倒计时答完一题可以立刻看解析考试模式有倒计时且答题过程中不能直接看正确答案交卷后才统一展示解析和得分。这两个逻辑如果混在一个页面里后期改动会非常痛苦所以源码里把它们拆成两个独立页面但把题目渲染、选项点击、答案判断这部分抽成了公共组件。组件化的好处很直接以后要加“判断题”“多选题”“材料题”等新题型只需要在question-card内部增加对应模板分支而不需要动页面结构。这也算是做练题类小程序的基本功。2.2 数据模型与题库 JSON 格式题库数据结构是整套系统最核心的部分。我见过很多半成品源码把题目字段写成option1、option2、option3、option4看着简单实际用起来非常难受扩展一道 5 个选项的题就得改表结构。所以这套工程里选项永远是一个数组。单题 JSON 结构如下{ id: q_10086, category: 科目一, type: single, content: 在道路上发生交通事故仅造成轻微财产损失并且基本事实清楚的当事人应当如何处理, options: [ 先保护现场再协商, 先撤离现场再协商处理, 将车停在原地等候处理, 打电话报警等候处理 ], answer: [1], analysis: 发生轻微财产损失且基本事实清楚应先撤离现场再协商避免堵塞交通。, difficulty: 2 }几个字段说说设计原因type字段支持single、multiple、judge三种题型。判断题虽然只有两个选项也可以直接用single表示但单独用judge是为了在 UI 上渲染成“正确/错误”按钮交互更明确。answer始终是数组多选题存入多个索引。单选也存成数组统一判断逻辑。这里我个人强烈不建议把答案存成字符串“A”“B”这种形式因为选项顺序一旦变化字母答案就失效了。analysis是解析文本练习模式下答完立即显示考试模式下交卷后显示。difficulty用于后续的智能组卷比如模拟考试可以按难度比例抽题。目前源码里默认是随机抽题但这个字段已经预留好了。练习记录表结构如下CREATE TABLE practice_record ( id INT PRIMARY KEY AUTO_INCREMENT, user_id VARCHAR(64) NOT NULL, question_id VARCHAR(32) NOT NULL, answer JSON, is_correct TINYINT DEFAULT 0, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_user_question (user_id, question_id) );错题表并不单独存题目内容只存question_id和加入时间题目内容仍然从题库表读取。这样设计的最大好处是如果题目内容有误运营人员直接在题库表里修正错题本里看到的自动就是新的内容。如果当初把题目冗余存进错题表修改一处要同步更新两张表很容易出现数据不一致。3. 实操搭建流程与关键步骤实现3.1 环境准备从压缩包到在微信开发者工具里跑起来这套源码的完整启动链路是HBuilderX 导入工程 → 配置 AppID → 运行到微信开发者工具 → 连接后端接口。一步一步说。第一步安装 HBuilderX。到官方下载地址拿最新稳定版建议不要用 Alpha 版插件兼容性在正式项目里更稳妥。安装完成后选择“文件 → 导入 → 从本地目录导入”选中解压后的2048-小程序文件夹。HBuilderX 会识别manifest.json并自动加载为 uni-app 项目。第二步准备微信小程序 AppID。在微信公众平台注册一个小程序账号类型可以是个人或企业个人账号功能受限较多但练题类应用个人也能过审。拿到 AppID 后在manifest.json的“微信小程序配置”里填进去。这里注意AppID 不填的话微信开发者工具只能用游客模式很多接口比如wx.login都会被限制。第三步配置后端接口地址。打开api/request.js找到BASE_URL常量默认指向http://localhost:3000/api。如果你用模拟器调试本地 Node 服务可以直接用这个地址但如果要用真机预览就必须改成电脑的局域网 IP或者直接把后端部署到云服务器上。这个位置极其容易踩坑真机上 localhost 指向的是手机自己不是你的电脑所以手机预览永远请求失败。第四步HBuilderX 菜单栏点“运行 → 运行到小程序模拟器 → 微信开发者工具”。这个操作会自动编译 uni-app 项目为微信小程序代码并打开微信开发者工具加载编译产物。如果没反应检查微信开发者工具是否开启了“服务端口”选项在微信开发者工具的“设置 → 安全设置”里打开。整套流程我第一次跑通大概用了二十分钟多数时间耗在 AppID 和端口配置上。现在这套源码我会建议你把manifest.json里的 AppID 直接替换为自己的再编译避免团队里多人协作时互相覆盖。3.2 题库导入不写一行代码也能批量上传题目再好的前端交互没有题目也是一具空壳。这套源码提供了两种题库导入方式。第一种是在后台管理页面里单题添加适合少量手工录入或做题目校正时使用。表单字段对应 JSON 结构前端提交后由服务端校验并写入题库表。这种方式对运营人员友好但批量录入效率太低。第二种是 Excel 转 JSON 批量导入。源码目录下有一个scripts文件夹里面放了import_questions.py脚本它可以把一个规范格式的 Excel 文件转换为题库 JSON 并分批请求导入接口。Excel 模板的列名固定为id, category, type, content, options, answer, analysis, difficulty。其中options列用|分隔选项answer列用英文逗号分隔多个答案索引。导入的核心脚本逻辑并不复杂我摘录关键代码来讲解import pandas as pd import requests df pd.read_excel(questions.xlsx, dtypestr) questions [] for _, row in df.iterrows(): q { id: row[id], category: row[category], type: row[type], content: row[content], options: row[options].split(|) if row[options] else [], answer: [int(x) for x in row[answer].split(,)], analysis: row[analysis], difficulty: int(row[difficulty]) if str(row[difficulty]).isdigit() else 1 } questions.append(q) for i in range(0, len(questions), 50): resp requests.post( f{BASE_URL}/admin/questions/import, json{questions: questions[i:i50]}, headers{Authorization: Bearer ADMIN_TOKEN} ) print(fbatch {i//50 1}: {resp.status_code})这里有几个细节值得注意。一个是空值处理Excel 里analysis允许为空但如果直接读成nan写入数据库时会变成字符串nan用户看到解析区域显示一个“nan”非常尴尬。所以代码里需要统一做空值转空字符串处理。另一个是分批提交单次接口请求体不要太大50 条一批是比较稳妥的值太大会触发服务端网关超时。3.3 答题逻辑与考试计时前端最容易写乱的两个地方答题页的核心逻辑无非就是展示题目、监听选项点击、判断答案、跳下一题。但实际编码时有两个地方容易写乱一个是多选题答案比对另一个是考试倒计时与提交的竞态处理。先讲多选题比对。如果直接把用户选中的索引数组和正确答案做比较顺序不一致就会误判。正确做法是对两个数组排序后再比较或者转成 Set 之后逐个判断成员关系。我在utils/answer.js里封了一个方法export function isAnswerCorrect(userAnswer, correctAnswer) { if (!Array.isArray(userAnswer) || !Array.isArray(correctAnswer)) { return false; } if (userAnswer.length ! correctAnswer.length) { return false; } const set new Set(correctAnswer); return userAnswer.every((item) set.has(item)); }多选题一个经典坑是用户先选 A再选 B取消 A然后选 C。如果每次点击选项都立刻做答案比对用户会发现答案在“错误”和“正确”之间反复横跳。所以正确交互是多选题在点击“下一题”时才判断不要在选项点击事件里做实时判断。再讲考试倒计时。倒计时实现用的是setInterval每一秒减一到零时自动交卷。但用户可能在最后一秒刚好点击提交按钮这时如果两个逻辑都对submitExam发起请求就会重复提交。处理方法是在组件里定义一个submitted标志位一旦提交或自动交卷立即赋值为true后续请求直接 return同时clearInterval清除定时器。这套源码里这个标志位还被写进了 storage避免用户在交卷过程中退出页面又重新进入导致二次提交。考试交卷后的判分逻辑放在前端做生成一份answer_sheet提交给服务端。这样做的好处是用户交卷后立刻能看到分数不需要等待后端判分缺点是如果前端代码被篡改理论上可以伪造高分。对于内部培训考核这种场景无所谓但如果是严肃考试判分逻辑务必挪到服务端前端只负责提交答案数组。3.4 发布前必做的几个检查项小程序上线比网页严格得多我每次发布前都会按这份清单过一遍。第一合法域名。微信小程序正式环境不允许请求http://localhost或任意 IP必须使用 HTTPS 域名并且在微信公众平台后台的“开发管理 → 服务器域名”里配置 request 合法域名。前端代码里出现http://的接口地址在开发者工具中可以勾选“不校验合法域名”来调试但发布体验版和正式版时这个开关是不生效的。第二隐私协议。微信官方现在要求小程序必须配置用户隐私保护指引尤其是涉及用户头像昵称、手机号、位置等信息的。练题类小程序至少会在登录时获取微信用户头像和昵称这一步必须在隐私协议里说明收集目的。否则审核会被驳回而且真机上调用wx.getUserProfile也可能拿不到数据。第三类目选择。教育类的练题小程序通常选“教育 - 在线教育”或“教育 - 教育信息服务”。如果涉及收费课程或考试报名资质要求会提高。如果只是纯粹的刷题工具个人主体注册的小程序也能过审但要注意不能出现诱导分享、虚拟支付等违规操作。第四分包与包体大小。虽然题库走了接口但前端页面代码还是要留意体积。我用这套源码实测编译后主包在 1.4MB 左右如果加了不少本地图标和调试页面可能冲到 1.9MB。建议把exam、stats这类低频页面放进分包subPackages指定root: pagesSub这样主包降下来性能也有提升。第五动态设置标题。不同分类页面的标题如果写死在pages.json里用户进入“科目一”和“科目二”看到同一个标题体验很糙。这套源码在onLoad里调用了uni.setNavigationBarTitle({ title: categoryName })但有个坑如果是通过 tabBar 页面跳转onLoad只触发一次二次进入不会更新。解决办法是把标题设置放在onShow里执行虽然会重复调用但成本很低胜在稳定。4. 调试、抓包与常见问题排查实录4.1 小程序抓包用 Charles 定位接口问题的完整思路开发练题小程序时最烦人的问题不是前端报错而是接口通了但数据不对或者接口没通但报错信息被前端吞了。这时候就需要抓包看真实请求。我先说为什么必须会抓包。微信开发者工具的 Network 面板确实能看到请求但它只能看工具环境下的请求。真机预览或者线上问题时工具里看不到只能借助抓包工具。这套源码的接口层做了一层uni.request封装所有请求都走了同一个入口理论上可以通过日志定位但真机上没法直接看 console抓包就成了最高效的手段。我用的是 Charles流程如下第一步电脑和手机连同一个 Wi-Fi并确保两台设备的 IP 能互相访问。打开 Charles菜单选 “Proxy → Proxy Settings”勾选 HTTP Proxy端口默认 8888。第二步手机设置代理。iPhone 在 Wi-Fi 设置里手动配置代理填写电脑局域网 IP 和端口 8888。安卓手机路径类似不同品牌略有差异。第三步安装 Charles 根证书。这一步是为了解密 HTTPS 流量。手机上用浏览器访问http://charlesproxy.com/getssl下载并安装证书。iPhone 还需要在“设置 → 通用 → 关于本机 → 证书信任设置”里把 Charles CA 证书的完全信任开关打开。安卓 7.0 以上系统默认不信任用户安装的证书微信小程序请求大概率仍然解密失败这种情况可以考虑用低版本安卓机或者在开发者工具里调试。第四步在 Charles 里找到微信小程序的请求域名右键选择 “Enable SSL Proxying”然后刷新小程序页面就能看到完整的请求和响应内容了。我通常会重点关注请求体里的user_id、question_id是否正确响应体里的status_code和msg是什么。抓包最大的价值是能撕开前端封装这层窗户纸直接看底层数据。比如用户反馈“模拟考试交卷后分数是 0”前端代码看起来没毛病一抓包发现提交的answer_sheet里题目 id 全部为空因为题目列表还未加载完就点了交卷这种问题不看数据流根本猜不到。实际上这个问题是考试页在页面onLoad后立即初始化questions数组但拉接口是异步的在数据返回前用户快速点击交卷就会以空数组提交。源码里为此加了一个isLoading状态为true时交卷按钮置灰不可点。4.2 常见报错与排查速查表把这段时间里被问得最多的问题整理成一张速查表项目里遇到类似情况可以直接对照排错。报错/现象可能原因解决办法request:fail url not in domain list后台未配置合法域名或本机调试未关闭校验微信开发者工具勾选“不校验合法域名”正式发布必须在后台配置 HTTPS 域名app.json: [tabBar] field item pagePath找不到新增页面后未在pages.json注册检查页面路径是否全部加进了pages.json和tabBar.list手机预览白屏BASE_URL仍为 localhost改为电脑局域网 IP 或线上域名重新编译登录后头像无法显示wx.getUserProfile返回的临时 URL 失效保存头像时调用uni.uploadFile存到自己的服务端不要直接引用临时链接题目图片加载很慢图片没用 CDN或者小程序主包内嵌了大图图片上传到对象存储并开启 CDN代码中通过https引用避免 base64 存储倒计时到 0 但页面卡住定时器在页面卸载时未清除在onUnload中调用clearInterval并且交卷逻辑加submitted防重复错题本重复数据无唯一索引判断是否已存在后才插入失败错题表对(user_id, question_id)加唯一联合索引插入前先查或使用“不存在则插入”语法导航栏标题首次正确返回后变默认动态设置标题写在了onLoad改成在onShow中重设标题这里多说一个容易被忽略的坑wx.login获取的 code 只能用一次且五分钟后过期。很多人在用户登录后把 code 存在本地半小时后再请求后端换取 openid结果必然失效。正确做法是每次调用wx.login获取新 code 后立刻传给后端换取登录态后端返回自定义 token小程序侧保存 token后续请求都带 token过期后跳回登录页重新登录。还有一个产品层面的坑和“更新题库”有关。客户端拉取题目后默认缓存 24 小时但如果后台刚改了某道题的解析用户本地缓存里还是旧内容。这套源码设计了一个简单的版本号机制后台每次更新题库维护一个question_version字段客户端在启动时拉取该版本号与本地缓存的版本号比对不同则清除本地题目缓存、重新拉取。这种策略比每次从服务端比对单题更省流量也更容易理解。4.3 列表分页、动态标题和导航栏高度适配练题小程序里还有一个高频场景题库分类列表或者错题本列表。数据量大了以后一次性渲染所有题目会导致页面卡顿特别是低端安卓机。这套源码在错题本和收藏夹列表使用了onReachBottom触底分页加载。思路是维护一个page参数每次触底page 1请求接口时带上page和pageSize返回后追加到列表数据尾部同时判断返回条数是否小于pageSize是则标记hasMore false不再发起请求。分页加载有一个隐藏问题翻页过程中用户可能对列表项进行删除操作比如从错题本移除某题。此时列表数据和当前页码会错位继续加载下一页可能漏掉一条。我的处理方法是删除后先本地移除列表项不增加页码仍然按当前页请求下一页。严格来说还不够严谨但多数场景下够用。再说动态标题。按摩托车单独说一个实际项目里遇到的案例有个用户反馈“产品助手”小程序里查不到数控设备型号980tdc。排查第一反应是搜索功能坏了但抓包后接口返回正常问题出在后台题库/设备型号表里根本没有录入这个型号。这不是代码 bug而是数据缺失。这种问题在练题类项目里也很典型用户反馈某道题看不到先查数据库有没有这条记录别急着改逻辑。顶部导航栏高度适配是另一个容易被忽略的点。iPhone X 及以上机型的底部有 Home Indicator页面底部按钮如果不做安全区适配会被手势条挡住。源码里在考试交卷按钮外层加了一个padding-bottom: constant(safe-area-inset-bottom)和env(safe-area-inset-bottom)的适配。同时导航栏高度在不同机型上也不一样用uni.getSystemInfoSync()里的statusBarHeight计算占位保证自定义导航栏内容不顶到状态栏。这套源码默认还是用微信自带导航栏所以这个适配问题主要在自定义导航栏的场景下才需要考虑。5. 二次开发方向与开源授权说明5.1 从练题到考试系统后端如何换成 Spring Boot MyBatis这套源码默认后端是 Node.js 写的但我知道不少开发者所在团队的主力技术栈是 Java尤其发包方动不动就指定 Spring Cloud 或者 Spring Boot。所以我在交付时额外提供了一个springboot-backend分支的参考实现基于 Spring Boot 3 MyBatis-Plus提供以下接口POST /api/auth/login GET /api/questions?categorypagepageSize GET /api/questions/random?count20 POST /api/records/batch GET /api/records/stats POST /api/wrong/add DELETE /api/wrong/remove GET /api/wrong/list?pagepageSize POST /api/exam/submit GET /api/exam/results/{userId}前端唯一要改的是api/request.js里的BASE_URL和接口路径适配。如果你要把题库管理后台也接进去可以在 Spring Boot 里加一个QuesionController使用 MyBatis 的Mapper注解对应前文提到的题库表结构。换后端时有一个容易忽略的差异数据库是 MySQL 5.7 还是 8.0会影响 JSON 字段的使用方式。MyBatis 在操作TINYINT转 Boolean 时也可能出现类型映射问题。我在后端分支里统一把is_correct定义为整数Controller 层使用Integer接收避免 MySQL 驱动版本不同带来的兼容性坑。5.2 源码使用边界全开源不代表没有约束每次开源交付都会遇到一个问题很多人觉得“全开源”就是无限制使用实际上还是要看授权协议。这套源码使用的是 MIT 协议可以免费商用、修改、再分发但需要在项目保留原版权声明。也就是说你拿这套源码去接外包或者做自己的产品完全没问题但不要在代码里把作者信息删掉后冒充原创再卖一遍这不符合开源精神的预期。如果你打算在这个基础上做商业产品有几点建议一定要换掉默认的界面配色和 Logo。开源源码最大的风险是“撞脸”同一个模板被别人拿去用了审代码时一眼就能看出来。哪怕是换个主色调、改一下首页 slogan也能降低同质化概率。后端接口要做好权限控制。题库导入接口别裸奔在外网加一个管理后台的登录拦截至少要用 JWT 或简单的 token 鉴权否则别人抓包拿到导入接口后分分钟给你灌几千道垃圾题。用户数据备份要重视。刷题记录和错题本是用户的核心资产服务端要定期备份数据库建议至少每天一次全量备份。小程序端也要有“清理缓存”的入口数据同步逻辑要写清楚避免用户误以为记录丢了。关于盈利模式练题类小程序常见的玩法包括设置付费题库分类、每月 VIP 去广告、给线下培训机构做私有化部署。微信小程序个人主体不能开通微信支付所以如果要收费要么升级企业主体要么走“联系客服/公众号下单后由后台手动发放激活码”的形式。这个限制在规划阶段就要想清楚免得功能做完再被卡一道。5.3 内容扩展别把练题工具做成“题库播放器”最后聊一点产品层面的思考。很多做练题类小程序的人容易把全部精力放在题库上想着“我题多我就赢了”。实际上用户留存的痛点往往在题目之外的体验上。比如答完题能不能看到分类正确率折线图这套源码里用 canvas 画了一个简单的正确率趋势图虽然代码不多但用户会觉得“这个工具懂我”。错题本能不能按掌握程度分组我后来加了一个“已掌握”标记用户点击后题目从错题本里移至“已掌握”列表这样错题本越来越薄用户复习时有成就感。每日一练能不能设置提醒小程序没有真正的推送除非用户主动打开“订阅消息”授权。这套源码在每日一练页面加入了一个“开启打卡提醒”的按钮利用wx.requestSubscribeMessage实现一次性订阅消息用户授权后平台会按模板向用户发送练习提醒。这个功能对日活提升非常明显建议重点做。我自己做项目最大的体会是题库类小程序的护城河不是功能多而是数据全、更新快、体验顺。开源源码能帮你省掉基础搭建的时间但后期的运营和迭代才是真正拉开差距的地方。根据我几次交付这个项目的经验最后再分享一个最实用的小技巧在本地开发时一定开一个浏览器的 H5 编译版本也就是 HBuilderX 里“运行到浏览器”。小程序里拿不到wx.login等原生能力H5 调试时可以先用模拟登录但页面布局、答题交互、倒计时这些核心逻辑在浏览器里调试效率比在微信开发者工具里高很多。等浏览器里跑通了再编译到微信开发者工具做真机验证。这条工作流帮我省了至少三分之一的时间强烈建议你也试试。