零后端微信小程序实战:口算练习小程序的架构设计与上线全流程
一个面向幼小衔接与小学低年级家长的练习小程序,也是博主自己小程序开发的业务尝试。
技术栈:微信原生小程序 · 无后端 · 纯本地存储 · 零网络请求
最终包体 544KB,主包上限 2MB,占用 27%
目录
体验二维码:

一、功能架构设计
1.1 产品目标
先明确一件事:这个产品的第一目标是"快速上线并获得用户",不是"功能完整"。
这个定位直接决定了后面所有技术决策。当目标从"做个好产品"切换到"最快触达用户"时,优先级排序会完全改变:
| 常规做法 | 本项目的选择 | 原因 |
|---|---|---|
| 上后端,数据可同步 | 无后端,纯本地存储 | 省掉服务器、域名备案、接口开发三条链路 |
| 用跨端框架一套代码多端发 | 原生小程序 | 只发微信端,不为假设的跨端需求付成本 |
| 功能尽量全 | 刻意做窄 | 功能越少,审核越快、Bug 越少、越容易传播 |
核心功能目标是三条:
- 打开就能练 ------ 无注册、无登录、无引导页,冷启动即可答题
- 每天愿意再来 ------ 打卡天数、星星、连续记录构成轻量正反馈
- 错的题要记住 ------ 错题自动沉淀,练会才移出
1.2 功能清单
| 模块 | 能力 |
|---|---|
| 数值范围 | 滑杆 10~1000 任意档位(步长 5),5 个快捷档位 |
| 题型 | 加法 / 减法 / 加减混合 / 乘法 / 除法 |
| 题量 | 5 / 10 / 20 题可选 |
| 答题 | 大键盘输入、输够位数自动判题、答对答错差异化反馈 |
| 错题本 | 答错自动收录、按错次排序、连续答对 2 次移出 |
| 数据 | 打卡天数、星星、今日题数、正确率统计 |
| 分享 | 结果页带正确率与范围的动态分享文案 |
1.3 架构分层
整个项目分四层,自上而下依赖关系单向:
| 层级 | 包含模块 | 职责 | 约束 |
|---|---|---|---|
| 页面层 | 首页 / 练习页 / 结果页 / 错题本 / 使用说明 | 交互流程与页面状态 | 页面数保持精简 |
| 组件层 | 题目卡 / 数字键盘 / 进度环 | 纯 UI 渲染 | 不参与业务判断 |
| 逻辑层 | 出题引擎 / 存储层 / 配置中心 / 工具函数 | 出题、读写、格式化 | 纯 JS,不依赖小程序 API |
| 数据层 | 本地存储(错题本 / 打卡 / 统计) | 数据落盘 | 无网络请求 |
分层遵循三条原则:
- 逻辑层不依赖小程序 API ------ 出题引擎是纯函数,可以直接在 Node 环境里跑大规模抽样测试(后文有验证数据)
- 存储层是唯一的数据出入口 ------ 业务层不直接调用本地存储接口,未来接云开发只改这一个文件
- 组件不参与业务判断 ------ 键盘只抛出"输入 / 删除 / 确定"三种意图,判题逻辑留在页面层,组件可以随时替换而不动业务
这三条带来的直接好处是:每一层都能被单独替换或测试。
1.4 三个关键决策
决策一:为什么敢不上后端
很多小程序第一反应是"先搭个后端",但对这个产品,后端的收益接近于零,成本却很高:
| 上后端的成本 | 纯本地的收益 |
|---|---|
| 服务器 + 域名 + SSL 证书 | 零成本 |
| 域名 ICP 备案(7-20 工作日) | 无需备案 |
| 配置服务器域名白名单 | 无需配置 |
| 接口开发与联调 | 无接口 |
| 数据合规责任(要写隐私政策、要担责) | 数据不出手机,合规压力极低 |
而用户实际需要的东西------练习记录、错题本、打卡天数------全部是本机数据,跨设备同步对 5-8 岁孩子的口算练习几乎不是需求。
这个决策换来的是:审核材料最少、上线路径最短。
代价也很清楚:换手机数据就没了。但对第一版来说这是可接受的取舍。
关键设计:把存储层独立成一个模块,所有读写集中在此。将来真要接云开发,只需重写这一个文件,页面层零改动。这是"不提前付成本,但保留演进能力"的典型做法。
决策二:为什么用原生而非跨端框架
只发微信端时,跨端框架带来的是纯开销:额外构建层、更大的包体、更难排查的问题。
原生小程序包体最小、启动最快。不要在需求未验证前为假设的跨端需求付成本。
而且本项目的逻辑层是纯 JS、与框架无关------真到要跨端那天,这部分可直接复用,迁移成本可控。
决策三:渲染引擎用默认的 WebView
微信的 Skyline 渲染引擎性能更好,但在部分组件与样式上有差异。快速上线期,不引入额外的兼容性风险比性能优化更重要。等产品跑通了再评估迁移。
1.5 核心模块:出题引擎
这是整个项目技术含量最高的部分。四个运算各有各的坑,其中除法最容易做错。
加法与减法:先定结果,再拆数字
新手常见的写法是"随机两个数,然后判断是否超范围,超了就重新随机"。这个做法有两个问题:结果分布不均匀 (小数和大数的概率不同),而且范围越大重试率越高。
正确做法是反过来------先随机出"和",再把和拆成两个数。这样天然保证两数之和等于目标值,且不会超出范围。减法同理,先定被减数再定减数,保证结果不为负。
除法:反推生成,让"整除"成为结构性保证
这是最容易出错的地方。如果像加减法那样分别随机被除数和除数,绝大多数组合除不尽,答案会变成小数------这对低龄儿童完全不可接受。
正确做法是反推,分三步:
| 步骤 | 操作 | 目的 |
|---|---|---|
| 1 | 随机一个除数(≥2) | 排除"÷1"这种没有练习价值的题 |
| 2 | 随机一个商(≥2) | 排除"商为 1"的情况 |
| 3 | 把除数和商相乘,得到被除数 | 被除数由乘法得出,整除是必然的 |
关键在第三步:被除数不是"随机出来的",而是"算出来的"。所以整除不是碰运气,而是结构性保证的。
乘法:因数需要限制上限
乘法有一个隐蔽问题:如果两个因数都在整个范围内随机,那么当一个因数取到大数时,另一个因数只剩一种取值,题目会集中在少数组合上,失去随机性。
解决办法是把第一个因数限制在"范围开平方"以内,再根据剩余空间取第二个因数。这样乘积能覆盖整个范围,分布也更均匀。
同时因数都 ≥ 2,排除"×1"这类没有练习价值的题。另外会随机交换两个因数的位置,让"50 × 2"和"2 × 50"都能出现。
防死循环
生成题目时会做组内去重,但必须设一个尝试次数上限。当可选题目空间小于需求时,允许重复补齐,绝不卡死。
这一点在小范围下尤其重要:10 以内的乘法排除 ×1 后只有 4 个唯一乘积(4 / 6 / 8 / 9),要出 20 题必然重复,但页面不能卡住。
抽样验证结果
验证覆盖 4 个范围(10 / 50 / 100 / 1000)× 5 种题型 × 3 万次抽样:
| 检查项 | 异常次数 |
|---|---|
| 算式计算错误 | 0 |
| 减法出现负数 | 0 |
| 除法除不尽(出现小数答案) | 0 |
| 0 参与运算(如 5+0、6×0) | 0 |
| 出现 ×1 / ÷1 / 商为 1 | 0 |
| 结果超出所选范围 | 0 |
零异常。这套验证可以随时重跑,是后续改动出题逻辑时的回归保障。
1.6 核心模块:存储层
存储层看着简单,但它是数据损坏时唯一能保护用户的那层。
三条保护措施:
| 措施 | 作用 |
|---|---|
| 统一异常捕获 | 存储写满、数据损坏都不能让小程序崩溃,读失败返回默认值 |
| 读时补全字段 | 读取后与默认结构合并,缺失字段补默认值,避免旧数据导致渲染异常 |
| 写入合并 | 多次配置变更合并为一次写入,减少存储操作 |
更关键的是可选项校验。这里踩过一个真实的坑:
产品迭代中把题量选项从
10 / 20 / 50收敛为5 / 10 / 20,但没有同步加校验。结果:之前选过 50 题的用户升级后,首页选项一个都不高亮,但练习页照样出 50 题------界面和行为对不上。
修复方式是在读取配置时做枚举校验:如果存的值不在当前可选项列表里,就回退到默认值。
经验 :每次调整可选项列表(题型 / 题量 / 范围),都要同步检查存储层是否有对应的校验。可选项收敛会同时踩两个方向的坑------新值出不来、旧值出不去。
顺带说明:数值范围是连续的(10~1000 步长 5),不能按枚举校验,否则用户拖到 1000 会被打回默认值;它需要的是区间校验 + 对齐步长。这两类字段要用不同的校验策略。
1.7 性能设计
| 措施 | 说明 |
|---|---|
| 题组不进页面状态 | 题目数组挂在实例上,界面更新只传"当前题 + 计数器",避免整组数据反复跨 JS-Native 桥 |
| 批量更新 | 每次切题合并为一次状态更新(题面、输入、反馈、进度一次传完) |
| 滑杆拖动不落盘 | 拖动过程中只更新显示值,松手才写存储,避免高频写造成卡顿 |
| 拖动中不回写控件值 | 否则与拖拽手势冲突,滑块会抖动 |
| 按需注入 | 开启后首屏只加载首页代码 |
| 多位数排版自适应 | 算式宽度差异极大(3 + 4 = 与 1000 - 999 =),按最大数字位数分 4 档降字号 |
| 定时器统一清理 | 页面销毁时清掉所有延时任务,杜绝异步回调打到已销毁的页面 |
1.8 交互层面的取舍:为什么答错停留更久
答题反馈的停留时长是刻意区分开的:答对约 0.7 秒,答错约 1.6 秒。
答对时快速推进,维持节奏感;答错时需要更长时间看清正确答案。这个细节对低龄用户很关键。
还有一个隐蔽问题:一位数答案极易误触------孩子按下第一个键就触发了自动判题,没有纠错机会。所以自动判题的延迟按答案位数区分:一位数答案给更长的撤销窗口(约 0.7 秒),多位数需要连按多次、不存在误触,保持短延迟(约 0.4 秒)以维持节奏。
另外,练习中途退出会二次确认,避免孩子误触返回丢掉整组进度。
二、如何发布一个微信小程序
发布流程本身不复杂,但有三个容易搞错的认知,每一个都可能导致白忙一场。
2.1 先纠正三个认知
❌ 误区一:"我们没有服务器,所以不用备案"
错。 这是两件完全不同的事:
| 微信小程序备案 | 域名 ICP 备案 | |
|---|---|---|
| 是什么 | 平台侧主体登记 | 工信部网站备案 |
| 谁要做 | 所有小程序,无一例外 | 只有用了自有服务器 / 域名的才做 |
| 在哪办 | 小程序后台「去备案」 | 云服务商备案系统 |
| 费用 | 免费 | 免费(但需买服务器) |
本项目纯本地存储、零服务器,不需要域名 ICP 备案,但微信小程序备案必须做------不做无法发布。
好消息:小程序备案不需要买服务器,比域名备案简单得多。坏消息:要 5-20 个工作日,是全流程真正的瓶颈。
❌ 误区二:"教育类小程序就该选教育类目"
不一定,而且选错是驳回头号原因。
微信的教育类目分层很细:「在线视频课程」需要办学许可证或视听节目许可证,「学历教育(培训机构)」需要区县级《民办学校办学许可证》加营业执照。K12 学科类培训更是监管红线。
但「口算练习工具」的本质是随机出题加自动判题------免费、无课程销售、无教学视频、无师资,工具属性远强于教学属性。
建议按「工具」类目提审(如工具 → 计算器,"适用于提供计算工具等"),无资质要求,过审最稳。
❌ 误区三:"审核通过就等于上线了"
错。 审核通过后必须手动点击「发布」,小程序才会出现在搜索结果里。
这是新手最常问的"为什么审核通过了还搜不到"。
2.2 主要关键步骤
阶段一:注册账号(决策不可逆)
- 访问 mp.weixin.qq.com → 右上角「立即注册」→ 选「小程序」
- 填写一个全新邮箱(从未注册过公众平台、未绑定过个人微信号)
------ 注意:一个邮箱只能注册一个小程序 - 去邮箱点击激活链接,或回填 6 位验证码
- 小程序类目选「其他」(游戏类才选游戏,且一级类目之后不可改)
- 选择主体类型 ------ 提交后无法修改
主体类型对比:
| 个人 | 个体工商户 | 企业 | |
|---|---|---|---|
| 成本 | 0 元 | 30 元/次(限时优惠) | 300 元/次 |
| 微信支付 | 不支持 | 支持 | 支持 |
| 流量主 | 仅基础广告位 | 全量 | 全量,填充率最优 |
| 添加到桌面 | 受限 | 支持 | 支持 |
如果要长期运营甚至变现,建议直接上个体工商户或企业主体。 个人主体虽然 0 元起步,但流量主只有基础广告位,且"添加到桌面"能力受限------后者对"每天练"的打卡类产品是最值钱的入口。
注意 :填企业名称时必须与营业执照一字不差 ,包括
(个体工商户)这类后缀,且必须用全角括号。这是第一大驳回原因。
阶段二:完善信息(决定审核成败)
- 设置名称、头像(≥144×144)、简介(≤120 字)
------ 名称每年只能改 2 次,且改名会触发重新认证、再交一次费,一次想好
------ 建议埋搜索关键词,如「口算」「算术」「加减法」 - 添加服务类目(建议工具类目)
- 配置用户隐私保护指引
路径:设置 → 服务内容声明 → 用户隐私保护指引
------ 哪怕完全不采集个人信息也必须填写并公布,否则直接驳回
阶段三:小程序备案(立刻启动,这是瓶颈)
- 后台首页点「去备案」
- 填写主体信息 ------ 通讯地址必须详细到门牌号,否则驳回
- 填写小程序基础信息,服务内容标识与实际功能匹配
- 下载承诺书签署(企业盖章 / 个人手写签名按手印)
- 管理员人脸核身(白墙、光线充足、不戴帽 / 镜 / 口罩)
- 平台初审,1-2 个工作日
- 工信部短信核验 ------ 24 小时生死线
- 通管局审核 1-20 个工作日,下发备案号
第 15 步是硬时限 :初审通过后工信部(号码 12381 )会发送核验短信,收到后 24 小时内必须登录工信部备案系统完成核验,超时自动驳回、从头再来。
检查手机是否拦截了 12381 的短信------这条短信躺过夜就白等一周。
阶段四:开发配置
- 获取 AppID:开发 → 开发管理 → 开发设置 → 开发者 ID
- 填入项目配置文件
- 添加体验成员:管理 → 成员管理
- 真机全流程验证
本项目因零后端架构,以下全部跳过:
- 配置服务器域名 / 业务域名 / WebSocket 域名
- 申请 SSL 证书
- 域名 ICP 备案
- 提供测试账号(无需登录即可体验全部功能)
阶段五:上传与提审
- 开发者工具右上角「上传」,填写版本号与备注
- 后台「管理 → 版本管理」→ 找到开发版本 →「提交审核」
- 填写审核信息(服务类目、功能页面、测试账号)
- 等待审核 1-3 个工作日
- 审核通过后手动点「发布」
版本功能说明建议这样写(降低审核员疑虑):
本程序是一款免费的口算练习工具,为小学低年级儿童提供 10~1000 以内加减乘除随机出题与自动判题功能,含错题本与打卡记录。
不涉及在线课程销售、不涉及学科类培训服务、不收集任何个人信息,所有数据仅保存在用户手机本地,无服务器、无网络请求。
无需登录即可完整使用全部功能。
2.3 时间线
| 时间 | 事项 |
|---|---|
| D1 | 注册账号(个人当天完成 / 企业认证 1-5 个工作日) |
| D1 | 立刻启动备案 ← 关键路径 |
| D1-D3 | 完善名称 / 头像 / 简介 / 类目,配置隐私指引 |
| D2-D5 | 真机调试(与备案并行,不必串行等待) |
| D5-D20 | 等备案(盯紧 12381 短信) |
| 备案通过 | 上传代码,提交审核(避开周五) |
| +1-3 天 | 审核通过,手动点「发布」,上线 |
关键路径是备案,不是开发。 代码可能一周就写完了,真正卡时间的是等管局。
2.4 常见驳回原因对照
| 驳回原因 | 应对 |
|---|---|
| 隐私保护指引未配置 | 勾选"不收集"并公布(最常见) |
| 类目与内容不符 | 改用工具类目 |
| 主体名称与执照不一致 | 逐字照抄,含全角括号后缀 |
| 备案地址不详 | 详细到门牌号 |
| 人脸核身失败 | 白墙加光线充足,不戴帽 / 镜 / 口罩 |
| 12381 核验超时 | 24 小时内核验,设闹钟 |
| 名称侵权或绝对化用语 | 提前查重,避免"最""第一" |
三、效果图与源码
3.1 效果图
三张核心界面的效果图见同目录下的 效果图.html,浏览器打开即可预览,可直接截图作为配图。
首页

自上而下的信息层级:
- 橙色 Hero 卡 ------ 产品名,加上今日已练 / 连续天数 / 获得星星三项数据
- 数值范围滑杆 ------ 大号数字实时预览当前档位,下方是场景提示(如"提高 · 两位数熟练运算")
- 快捷档位 ------ 5 个等分按钮(10 / 30 / 50 / 100 / 500),解决 199 档难以精确定位的问题
- 题型与题量 ------ 均为单行等分按钮,决策成本降到最低
- 开始练习,以及错题本入口
设计细节:数值范围的提示文案会随题型变化。同样拖到 100,加减法显示"两位数熟练运算",乘法显示"表内乘除 · 九九口诀范围"------用同一套文案会产生误导。
练习页

- 顶部:当前配置与进度条(第几题 / 共几题)
- 中部:大号算式与输入区,字号按最大数字位数自适应
- 底部:大键盘,删除与确定用不同配色区分
结果页

- 正确率环与分档评语
- 四项数据:答对 / 答错 / 总用时 / 平均每题
- 错题列表(已自动收进错题本)
- 分享引导
3.2 源码结构
根目录
| 文件 | 说明 |
|---|---|
app.js |
启动时加载设置与学习档案 |
app.json |
页面注册、窗口配置 |
app.wxss |
全局样式与设计变量 |
project.config.json |
开发者工具配置 |
sitemap.json |
微信索引配置(全量允许,利于搜索曝光) |
页面(pages/)
| 目录 | 说明 |
|---|---|
index/ |
首页:范围 / 题型 / 题量配置 + 数据概览 |
practice/ |
练习页:答题主循环(核心) |
result/ |
结果页:成绩 + 错题 + 分享 |
wrongbook/ |
错题本 |
about/ |
使用说明 + 数据合规声明 |
组件(components/)
| 目录 | 说明 |
|---|---|
question-card/ |
题目卡(纯展示) |
keypad/ |
数字键盘(只抛意图) |
progress-ring/ |
进度环 |
逻辑层(utils/)
| 文件 | 说明 |
|---|---|
constants.js |
全部可调参数集中处 |
generator.js |
出题引擎(纯函数,可独立测试) |
storage.js |
存储层(未来接云的唯一改造点) |
util.js |
格式化 / 评语 / 文案 |
文档(docs/)
| 文件 | 说明 |
|---|---|
架构设计.md |
完整架构文档 |
效果图.html |
三张界面效果图 |
技术博客-口算小程序架构与上线实战.md |
本文 |
3.3 核心实现要点
几个值得说明的设计,按重要性排序:
出题引擎的四个约束。 加法和减法先定结果再拆数字;乘法把第一个因数限制在范围开平方以内,避免题目集中;除法反推生成,先定除数和商再相乘得被除数,让整除成为结构性保证;所有运算都保证参与计算的数不为 0,并排除 ×1 与 ÷1。
存储层的两类校验策略。 数值范围是连续值,用区间校验并对齐步长;题型和题量是离散选项,用枚举校验。两类字段不能混用同一种策略------把连续值按枚举校验会导致用户拖到上限被打回默认值。
滑杆的性能处理。 拖动过程中只更新显示数值,松手才写存储;并且拖动期间不回写控件的值,否则会和拖拽手势冲突导致滑块抖动。
多位数排版自适应。 算式宽度差异极大,根据最大数字位数分 4 档降字号。同时要考虑用户已输入的位数------答案虽然是个位数,但孩子乱按出多位时,输入区同样需要缩字号,否则会撑破布局。
等分布局的一个必备细节。 题型、档位、题量三行都用了等分铺满屏宽。等分布局必须给子项设置最小宽度为 0,否则子项会被内容撑开、等分直接失效。这个小细节在本项目里踩到了三次。
3.4 快速开始
- 下载并安装微信开发者工具(官方文档有下载入口)
- 打开工具,导入项目,选择项目根目录
- AppID 选「测试号」即可预览;正式发布时在项目配置中填入真实 AppID
- 点击「预览」,扫码即可在手机上体验
所有可调参数集中在 utils/constants.js,改一个文件即可,无需翻页面代码。
3.5 可调参数一览
| 参数 | 当前值 | 说明 |
|---|---|---|
| 滑杆下限 / 上限 | 10 / 1000 | 数值范围边界 |
| 滑杆步长 | 5 | 共 199 档 |
| 快捷档位 | 10, 30, 50, 100, 500 | 覆盖各学段常用跨度 |
| 题型 | 加法 / 减法 / 加减混合 / 乘法 / 除法 | 每项对应一组运算符 |
| 题量 | 5, 10, 20 | 每组题目数 |
| 默认题量 | 10 | 首次进入的预设值 |
| 错题移出阈值 | 连续答对 2 次 | 防止蒙对一次就过关 |
| 答对停留 | 约 0.7 秒 | 维持节奏感 |
| 答错停留 | 约 1.6 秒 | 留出看正确答案的时间 |
| 一位数答案撤销窗口 | 约 0.7 秒 | 应对误触 |
总结
回头看,这个项目真正值得记录的不是代码量,而是三组取舍:
- 范围与深度的取舍 ------ 砍掉后端、砍掉登录、砍掉社交,只留"出题 + 判题 + 错题本",换来最快的上线路径
- 远期与当下的取舍 ------ 不为"万一要跨端""万一要排行榜"提前付成本,但把存储层独立出来保留演进能力
- 可维护性与便利性的取舍 ------ 逻辑层纯函数化、可调参数集中化、符号映射集中化,让后续迭代(比如把题量从 10/20/50 改成 5/10/20)只是一行数据改动
如果这篇对你有帮助,欢迎在评论区聊聊你做小程序时踩过的坑。
最后提醒一句:上线前务必先配好用户隐私保护指引------这是儿童类小程序最常见的驳回原因,而且不花任何成本。