拓冰建站拓冰建站
首页 / 资讯中心 / 正文

uniapp考试答题小程序zip包:详解拆包改造到上架全流程

简介基于uniapp开发的考试答题类小程序源码包面向正在学习跨端开发、需要搭建答题类应用原型的前端初学者。项目覆盖单选题、多选题和判断题三种常见题型的完整作答流程支持上一题/下一题切换题目数据以静态方式存储适合脱离后端快速跑通界面与交互逻辑。包内共223个文件核心代码以Vue组件、JavaScript逻辑和CSS样式为主另有少量图片及工程配置文件压缩包仅1.47MB结构清晰可导入HBuilderX直接查看运行效果。该资源已有1329人学习浏览。通过阅读源码可以掌握uniapp的组件组织、事件绑定、页面数据管理以及多题型切换的交互设计方法也能了解本地静态题库在前端项目中的写法为后续接入真实接口、扩展交卷与判分功能提供实用参考同时可借鉴其目录组织方式快速定位页面、逻辑与样式模块。1. 为什么考试答题小程序常以 Zip 形态交付拿到一个uniapp开发的考试答题类小程序.zip先别急着解压跑起来。这个压缩包基本对应三种可能完整 uni-app 源码工程、HBuilderX 可直接导入的项目、或打包后的微信小程序产物。多数情况下它是源码工程意味着你可以二次改题、换皮肤、改支付逻辑再通过 HBuilderX 云打包发布成微信小程序或安卓 App。考试答题类小程序是所有工具型小程序里相当难做的一类因为它不只有页面跳转还涉及倒计时、本地题库、答题卡、交卷判分、错题收集稍不注意就会在弱网或切后台时丢状态。适合看这篇文章的人是手里已经拿到一个类似 zip 包、想顺手改造或上线的开发者。你不需要重新发明轮子但必须知道 uni-app 在考试场景里的正确打开方式以及哪些代码一改就崩、哪些配置上架必查。下面按「拆包 → 跑通 → 改题 → 打包 → 查隐患」的顺序来讲这是 uni-app 小程序开发里最典型的一条完整链路。2. 拆包还原工程先分清源码包与编译产物的差异遇到.zip第一步永远是确认包里装的是什么。常见做法是解压后先看根目录有没有pages.json。有说明是源码工程只有app.wxss、app.json之类则是微信小程序原生包不能直接用 HBuilderX 打开跑。两者后续动作完全不同。2.1 用目录结构判断 Zip 类型解压后第一眼要看的关键文件是pages.json—— uni-app 路由与页面配置存在即为源码工程App.vue—— 应用入口文件含应用生命周期main.js—— Vue 实例化入口Vue 2 或 Vue 3 在此区分manifest.json—— uni-app 应用配置含 AppID、模块权限pages/—— 所有页面组件目录uni_modules/—— uni-app 插件市场安装的组件# 在解压目录下执行确认是否 uni-app 工程 ls -la | grep -E pages.json|manifest.json|App.vue # 查看 Vue 版本约束 cat package.json | grep \vue\ || echo 无 package.json依赖由 HBuilderX 内置有package.json且vue版本为 3.x是 Vue 3 工程没有package.json说明依赖靠 HBuilderX 内置编译器处理。Vue 2 与 Vue 3 在选项式 API、生命周期钩子、v-model绑定上差异不小。常见的坑是原工程基于 Vue 2你用 HBuilderX 新版本默认走 Vite 编译 Vue 3起来后页面白屏、this.$emit报错。查manifest.json里是否显式声明了 Vue 版本。2.2 用 HBuilderX 导入运行的最小步骤HBuilderX 导入源码工程只需要三个动作打开 HBuilderX → 文件 → 导入 → 从本地目录导入选择刚解压的文件夹。若代码里使用了uni_modules的插件首次运行会提示安装。# 如果工程带 package.json也可以在终端手动安装依赖 npm install接着在 HBuilderX 工具栏选择「运行 → 运行到小程序模拟器 → 微信开发者工具」。首次运行会要求填manifest.json里的微信小程序 AppID可先填写测试号。注意微信开发者工具需要开启「服务端口」设置 → 安全设置 → 服务端口 → 打开否则 HBuilderX 编译产物推不过去。提示运行到微信开发者工具前先确认pages.json里condition字段是否配置了启动页面。没有配置时默认打开pages.json中pages数组第一项考试类小程序通常把首页设为pages/index/index但很多模板会把pages/exam/exam放第一位导致每次启动直接进考试页。2.3 编译产物文件结构说明HBuilderX 运行到微信开发者工具时会在工程根目录生成unpackage/dist/dev/mp-weixin这是编译后的微信小程序产物。源码改动会在保存时增量编译。如果 zip 包内没有pages.json而是直接有unpackage目录那这个包是编译产物而非源码需要反向操作在微信开发者工具里直接导入unpackage/dist/dev/mp-weixin或build/mp-weixin目录。但产物包的逻辑代码已被压缩改写可读性差不适合二次开发。遇到这种情况建议退回索要源码包。调试阶段还应抽查unpackage/dist/dev/mp-weixin/app.js是否完整。有些打包者图省事把 HBuilderX 的unpackage目录直接打包分发而该目录包含大量中间缓存文件体积膨胀且无法跨机器增量编译。正确的源码交付应剔除unpackage、node_modules这两个目录只保留源码。2.4 题目数据是 JSON 还是数据库决定改造难度解压后注意看pages.json的注册页面数量。考试答题类模板通常有pages/index/index用户中心与考试列表、pages/exam/exam答题主页面、pages/result/result成绩页、pages/error/error错题本。若发现pages/exam/paper这样带paper的目录多半是题目以 JSON 文件加载即前端本地题目。前端本地题目意味着改题必须发版适用低频更新场景。若页面里有完整的request网络请求如uni.request({ url: https://xxx.com/api })则题库在后端。此类结构更常见于商业成品zip 交付时附带接口文档或直接内置 mock 数据。检查common/或utils/目录下的 JS 文件中baseUrl定义确认是直连还是经代理转发。3. 从答题流程到状态管理考试类页面特有的数据流考试答题页面与普通展示型页面最大的差别在于状态多且互相依赖。正在答第几题、已选选项、剩余时间、标记状态、交卷前是否还有未答题目这五个核心变量牵动几乎所有交互。工程里如果pages/exam/exam.vue里只用data()管理这五个变量答题卡点击切换题目时经常出现选中状态丢失或倒计时闪烁。uni-app 项目上接手类似包时优先理清这一层数据流再考虑样式与 UI。3.1 答题页初始化与倒计时实现答题页一般从onLoad接收考试 ID 或试卷 ID。工程里若是onLoad(options)拿到paperId后再从静态题库 JSON 同步取题核心代码骨架通常长这样// pages/exam/exam.vue export default { data() { return { paperId: , questionList: [], currentIndex: 0, // 当前题号索引 answerMap: {}, // 题号 - 所选答案 remainSeconds: 1800, // 倒计时剩余秒数 timer: null, markedSet: {} // 标记题号集合 } }, onLoad(options) { this.paperId options.paperId this.loadPaper() this.startCountdown() }, methods: { loadPaper() { // 常见模板是直接 require 本地题库 const paper require(../../static/data/paper_ this.paperId .json) this.questionList paper.questions }, startCountdown() { this.timer setInterval(() { this.remainSeconds-- if (this.remainSeconds 0) { clearInterval(this.timer) this.submitPaper() } }, 1000) }, chooseAnswer(questionIndex, value) { if (this.answerMap[questionIndex] value) { // 再次点击同一选项表示取消选择常见于单选交互 this.$set(this.answerMap, questionIndex, ) } else { this.$set(this.answerMap, questionIndex, value) } } }, onUnload() { // 页面卸载时清除定时器防止切换页面后倒计时仍运行 if (this.timer) clearInterval(this.timer) } }answerMap用对象键值对存储答案而不是用数组下标最大好处是题目乱序或跳转答题时不需遍历全量数组。$set必须使用否则新增的键不会触发视图更新选项高亮会呈现「点不到」的假象。倒计时用setInterval每秒减 1而不是在update钩子里计算时间差是因为小程序切后台时setInterval会被挂起恢复后继续执行适合考试场景。用时间戳差值的方式反而会在退后台期间把剩余时间全部消耗掉。3.2 答题卡组件与选中态同步答题卡通常是页面底部或左右抽屉的面板当前停留在第几题、哪些题已答、哪些被标记需要与主答题区双向同步。若在同一个工程里已经按此实现了组件化那么代码里应具备// components/answer-sheet/answer-sheet.vue 伪代码 props: { answerMap: { type: Object, default: () ({}) }, markedSet: { type: Object, default: () ({}) }, currentIndex: { type: Number, default: 0 } }, computed: { answerCardList() { return this.questionList.map((item, index) { return { number: index 1, answered: !!this.answerMap[index], marked: !!this.markedSet[index], active: index this.currentIndex } }) } }答题卡上数字的颜色散点灰色表示未答、高亮表示已选、右下角小点标识标记题。组件内不要直接修改props而是通过$emit(jumpTo, index)通知父页面切换currentIndex。这是 uni-appvue2与vue3的通用规则但在 Vue 3 组合式 API 工程里通常会写成definePropsdefineEmits语法差异需要适配。3.3 交卷判分策略前端本地判分与提交接口判分判分逻辑决定代码复杂度。若 zip 工程是纯前端判分通常会有以下实现methods: { submitPaper() { const rightMap this.paper.answerList let correctCount 0 this.questionList.forEach((item, index) { if (this.answerMap[index] rightMap[index]) correctCount }) const score (correctCount / this.questionList.length * 100).toFixed(1) uni.setStorageSync(exam_score, { score: score, total: this.questionList.length, correct: correctCount, answerMap: this.answerMap }) uni.redirectTo({ url: /pages/result/result?score score }) } }前端判分适合模拟考试与练习模式缺点是用户通过调试工具改answerMap就能作弊因此正式考试场景不会采用。代码中若是空submitPaper、预留uni.request注释说明原作者预留了服务端判分入口。接入时需要额外的签名机制至少应加上uni.getStorageSync(token)作为身份凭证与服务端校验用户提交时间与题目版本一致性。答题时长、切屏次数等风控数据也应一并传给后端否则交卷接口会频繁被刷。3.4 动态设置标题与系统导航栏的适配考试类页面最常被要求改的是导航栏标题。原工程如果想在考试进行中更新导航栏文本代码里通常是uni.setNavigationBarTitle({ title: 正在答题第 (this.currentIndex 1) 题 })这里考虑放在答题页面交互上。微信小程序导航栏高度在 iPhone X 之后加入了安全区HBuilderX 默认按 44px 处理手动设置导航栏样式时容易在刘海屏上产生遮挡。比较稳妥的做法是使用uni.getSystemInfoSync()获取statusBarHeight再对自定义导航栏做适配const systemInfo uni.getSystemInfoSync() const statusBarHeight systemInfo.statusBarHeight || 20 // 自定义导航栏高度通常为 44Android 部分机型为 48 const navBarHeight systemInfo.platform ios ? 44 : 48原生导航栏在这些适配细节上相对省心但考试类小程序需要显示倒计时在标题栏不少模板会使用自定义导航栏。如果解压后的代码自定义导航栏布局混乱优先用uni.getSystemInfoSync()做基准修正而不是去逐套适配机型。4. 改造题库资源与分包避免加载慢与超过 2MBmanifest.json里通常写着「微信小程序」的配置但考试小程序天生要面对一个门槛微信小程序主包体积限制 2MB。题目如果以 JSON 打包进主包超过 50 道题的图文混排基本就危险。zip 工程改造的重点之一就是把题库按分包处理。4.1 把本地题库拆到分包目录查看原工程是否有subPackages配置{ pages: [ { path: pages/index/index, style: { navigationBarTitleText: 考试中心 } } ], subPackages: [ { root: pagesExam, pages: [ { path: exam/exam, style: { navigationBarTitleText: 答题中 } } ] } ] }把答题页、题库 JSON、答题卡组件全部迁移到pagesExam分包目录用户从首页点击考试时通过uni.navigateTo({ url: /pagesExam/exam/exam?id id })进入。首次进入分包页面会有一段加载时间为优化体验通常在首页放一个 loading 提示。微信开发者工具中勾选「详情 → 本地设置 → 启用自定义处理命令」可看到分包体积占比。如果 zip 包里题库使用了static/data/下的 JSON移动位置后要同步修改require的相对路径。4.2 远程题库的缓存与版本更新机制网络题库接入时常见做法是用uni.setStorageSync缓存整份 JSON启动时检测版本号uni.request({ url: https://api.example.com/paper/latest?paperId this.paperId, success: (res) { const remoteVersion res.data.version const localVersion uni.getStorageSync(paper_version_ this.paperId) if (remoteVersion ! localVersion) { uni.setStorageSync(paper_ this.paperId, res.data) uni.setStorageSync(paper_version_ this.paperId, remoteVersion) } } })版本号是整体更新的关键。不少后端返回的题目列表详情里没有version字段前端自行拼上时间戳也可用。但注意uni.setStorageSync有 10MB 的总限制图文较多的题目 JSON 动辄几 MB缓存全量题库容易触顶。实际开发中对图片资源单独做 URL 存储不缓存 base64 图片数据。4.3 遍历题目选项时的性能优化考试页面选项多用v-for渲染但是当一页承载几十道题滚动时切题卡顿经常出现。排查原工程是否在data中放了大数组对象v-for的key是否有唯一值。常见模板里写:keyindex一旦插入新题或在选项中间调整顺序渲染就全部重建view classoption-item v-for(option, optIndex) in question.options :keyoptIndex :class{ active: answerMap[currentIndex] option.value } tapchooseAnswer(currentIndex, option.value) {{ option.label }}. {{ option.text }} /view考试题目从后端返回后基本不会变:keyindex在这个场景下危害较小。但若存在随机抽题或选项乱序功能key 就必须使用option.value而非optIndex。答题反馈高亮效果依赖answerMap[currentIndex]如果数据更新不上多半是answerMap的初始化在data()中没有声明对应的 keyVue 2 无法侦测新增属性this.$set在此已成为必写项。5. 打包发布前的压缩包检查项zip 交付的意义在这里体现你拿到的不只是代码还要交付给别人能解压即用。现实中常有这种情况本地跑得好好的发出去给别人一解压打开是白屏或编译报错。大多是压缩时没排除中间产物或uni_modules插件目录不完整。5.1 Zip 包内应排除的目录与文件使用zip命令打包 uni-app 源码工程这是标准的交付命令zip -r exam-app.zip . \ -x node_modules/* \ -x unpackage/* \ -x .git/* \ -x .idea/* \ -x *.log-x参数排除目录时通配符写法是/结尾连同目录一并剔除。常见错误是只排除了文件没有排除目录导致unpackage/dist/dev旧编译产物与新源码混淆接收方一打开 HBuilderX启动页面还是旧版本。node_modules在 uni-app 中属于可选依赖HBuilderX 会按package.json恢复但如果你安装了第三方 npm 包必须附带package.json和package-lock.json否则接收方npm install装出来的版本可能不同。5.2 Manifest 配置与微信小程序 AppID 登记uni-app 打包微信小程序的配置集中在manifest.json→ 微信小程序配置。重点检查是否有真实的 AppID而不是测试号。如果没有打包后无法上传真实版本。微信后台需要配置合法域名且 request 域名必须 HTTPS否则调试时可以「不校验合法域名」打开真机预览时直接请求被拦。具体配置路径如下表配置项位置说明微信小程序 AppIDmanifest.json → 微信小程序配置 → AppID不能为空或touristappidrequest 合法域名微信公众平台 → 开发管理 → 服务器域名必须 HTTPS不能带路径业务域名微信公众平台 → 开发管理 → 业务域名用于 web-view 嵌入 H5 页面不校验合法域名微信开发者工具 → 详情 → 本地设置仅开发调试用上线前必须关闭隐私协议小程序后台 → 设置 → 服务内容声明涉及收集用户答题记录即必须填写如果 zip 里附带了隐私协议弹窗组件但未做初始化常见做法是检查pages.json中是否有类似privacy的配置。只有当小程序收集用户信息时这个配置有实际意义。考试答题记录一般属于个人信息需要在小程序管理后台填写《用户隐私保护指引》。若忽略此步骤提审时大概率不过。5.3 uniapp 上架安卓应用市场的额外处理如果 zip 工程还包含App打包目标即 HBuilderX 发行 → 原生 App-云打包需要注意 uni-app 的 Android 打包策略。云打包时需要 DCloud 开发者证书首次打包生成 test 证书可直接运行但上架应用市场必须使用正式证书。此时 zip 包中是否包含app-plus相关目录显得重要manifest.json中「App 模块配置」需要选择相应权限如推送、地图等模块会增加包体积。Android 应用市场如华为、小米、OPPO要求提供软著或测试账号其中小米与 OPPO 等待审核周期较长。若 zip 工程中manifest.json的app-plus节点下没有distribute配置打包时要手动输入包名与版本号。包名与已有应用冲突也会导致上架失败需要注意。5.4 上传前用微信开发者工具做一次真机预览代码层面检查完毕最后一步是用预览二维码在真机上完整走一遍。考试类小程序重点测三个场景答题页切后台再恢复倒计时是否回退交卷瞬间断网数据是否丢失答题卡组件在高分屏下的滚动位置是否错乱。真机预览时打开微信开发者工具「真机调试」申明小程序处于调试模式时请求不校验域名。但调试模式性能低于正式版建议用「预览」二维码再走一次正式流程。确认考试流程完整后在后台提交审核时版本描述写「在线答题练习仅本次考试需提交答案」即考试中不会诱导点击广告也不涉及任何敏感内容。6. 用 ECharts 或 Canvas 生成成绩分析图提升答题结果页效用考试小程序的值钱之处在考后数据呈现。题目做完不能只显示一个分数被丢在一边。改造成绩页加入分数分布、知识薄弱点、正确率统计图表能显著延长用户停留提高回访率。这是拿到 zip 包后优化价值最集中的地方。6.1 在 uni-app 中配置 ECharts 的正确姿势uni-app 跨端环境不能直接npm install echarts后浏览器式使用。常见做法是使用 uni_modules 里的qiun-data-charts或lime-echart等插件底层仍使用 ECharts。如果原工程没有安装图表插件可考虑以下流程# 在项目根目录的 uni_modules 中安装推荐 # HBuilderX 右键 uni_modules 目录 → 从插件市场安装qiun-data-charts是所有图表统一封装考试场景主要使用雷达图和柱状图。雷达图展示各知识点正确率柱状图展示近十次考试趋势。加载完成后在结果页导入组件并注册template view classchart-wrapper qiun-data-charts typeradar :chartDataradarData :optsradarOpts / /view /template script import qiunDataCharts from /uni_modules/qiun-data-charts/components/qiun-data-charts/qiun-data-charts.vue export default { components: { qiunDataCharts }, data() { return { radarData: { categories: [单选, 多选, 判断, 填空, 简答], series: [ { name: 正确率, data: [85, 62, 91, 45, 30] } ] }, radarOpts: { color: [#1890FF], padding: [10, 10, 0, 10], dataLabel: false, yAxis: { min: 0, max: 100, splitNumber: 5 }, legend: { show: false } } } } } /scriptcategories对应纬度数据来自uni.getStorageSync(exam_analysis)在交卷时一并写入。判断哪些题属于哪个知识域需要题库 JSON 给每道题加category字段。zip 模板里若没有这个字段可以在本地临时映射将单选、多选等题型作为 top-level category后续再细化为知识点分类。这个做法保留成绩页图表的扩展度数据量上来后不再需要改组件接口。6.2 用户行为数据的本地聚合与展示除正确率外还有一个容易忽略的观察维度——做题速度。记录每道题的作答耗时考试结束后生成折线图可以直观看出耗时最高的题是什么。工程实现上可对chooseAnswer方法增加时间间隔记录chooseAnswer(questionIndex, value) { const currentTime Date.now() const cost this.questionStartTime[questionIndex] ? currentTime - this.questionStartTime[questionIndex] : 0 this.$set(this.timeCostMap, questionIndex, cost) this.$set(this.questionStartTime, questionIndex, currentTime) // 原答题逻辑... }图表组件展示时间数据时建议用柱状图而非折线图。一场考试的时间成本分布本来就是分段的柱状图每个题号的耗时独立呈现更贴合查看目的。控制图表高度滚动页面时图表性能不会太差。Canvas 在低端 Android 机器上的渲染开销确实存在所以图表数据量超过 30 个点时建议只展示最耗时的前 10 题而不是每题都塞进坐标轴。6.3 导出成绩分析页的注意事项最后一段代码检查记得覆盖App.vue里的全局错误捕获// App.vue onLaunch() { uni.onError((err) { console.error(全局错误:, err) }) }uni.onError捕获的是运行时 JS 错误不是接口错误。考试小程序最怕交卷现场白屏全局错误日志能辅助排查。Charts 配置出错通常抛TypeError: Cannot read property series of undefined此时radarData的字段命名需要对照组件文档逐一核对。按这种形态补全后成绩页从「看个分数就关闭」升级为「能看到问题在哪」这对用户留存和复购都会产生正的收益。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门