Skip to content

错题本开发遇到的麻烦

起因

小孩做作业,错题不少。老师要求整理错题本,把做错的题目抄下来,分析错误原因,定期复习巩固。

听起来是个好习惯,但实际操作起来问题一大堆:

1、抄题太费劲——一道数学大题抄下来要十几分钟,小孩嫌累不愿意写
2、拍照打印——用手机拍下来再打印,图片模糊、排版混乱,效果不好
3、电子版整理——用 Word 或笔记软件录入,公式输入困难,效率极低

于是想着做一个在线错题本工具:拍照或截图上传 → AI 识别题目内容 → 自动生成错题记录 → 支持分类标签和复习提醒。

想法很美好,开发过程却踩了一堆坑。

麻烦一:题目识别准确率低

最初方案是用 OCR 文字识别提取题目文字。实际测试发现:

  • 手写字体识别差:小孩的作业本上字迹潦草,OCR 识别率不到 60%
  • 数学公式无法处理:分数、根号、上下标等符号,OCR 全部识别成乱码
  • 图文混排噩梦:题目中穿插几何图形,OCR 要么漏掉要么位置错乱

后来改用多模态 AI(视觉语言模型)直接理解图片内容,效果好了很多,但成本也上来了。单次识别成本约 0.02~0.05 元,如果用户频繁使用,费用不可忽视。

最终做了个折中:优先尝试轻量级 OCR,识别置信度低于阈值时才调用 AI 模型兜底。

麦烦二:公式渲染是个深坑

识别出的题目包含大量数学公式,需要在前端正确渲染展示。这块坑特别多:

LaTeX 解析问题

AI 返回的内容常包含 LaTeX 格式的公式,如 \frac{1}{2}x^2 + \sqrt{3}。前端渲染 LaTeX 有几个主流方案:

方案优点缺点
MathJax成熟稳定体积大(~300KB),首次加载慢
KaTeX轻量快速部分复杂语法不支持
MathML原生支持兼容性差,Safari 渲染异常

选了 KaTeX,结果遇到各种边界情况:

  • AI 生成的 LaTeX 不规范,缺少闭合标签导致渲染失败
  • 分数嵌套过深时溢出容器
  • 中文字符与公式混排时间距异常

最终写了一大堆正则做预处理清洗,又加了容错机制:渲染失败时降级为纯文本显示。

移动端适配

手机屏幕窄,长公式自动换行后可读性很差:

原式:(a + b + c + d)(e + f + g + h) = ae + af + ag + ah + be + bf + bg + bh + ...

换行后变成一堆碎片,完全看不懂

解决方案是检测公式长度,超长公式启用横向滚动容器,并添加「展开/收起」按钮。

麻烦三:数据存储的纠结

错题本的核心价值在于数据积累和长期使用,存储方案必须慎重选择。

本地存储 vs 云存储

维度本地存储(IndexedDB)云存储
隐私安全数据不出浏览器需要用户信任服务端
多端同步不支持天然支持
开发成本需要用户系统、鉴权、数据库
数据风险清浏览器数据就没了可靠但依赖服务器

考虑到目标用户是学生家长,对隐私敏感度较高,最终选择了 本地优先 策略:

  • 默认使用 IndexedDB 存储在浏览器本地
  • 提供可选的云同步功能(需注册登录)
  • 支持导出 JSON / PDF 备份

IndexedDB 的坑

IndexedDB 用起来比 localStorage 复杂得多:

  • 异步 API:所有操作都是异步的,代码容易陷入回调地狱
  • 版本迁移:数据库结构变更时需要处理 upgrade 逻辑,处理不好会丢数据
  • 存储限额:浏览器通常限制几十到几百 MB,存多了会被清理
  • 无查询能力:没有 SQL 那样的查询语法,筛选排序得自己实现

封装了一个简易的 ORM 层才勉强能用的舒服点。

麻烦四:图片处理性能问题

用户上传的错题图片通常是手机拍摄的照片,分辨率高、文件大(3~8MB)。

直接上传和处理会遇到几个问题:

1、预览卡顿:大图在 canvas 上渲染时内存飙升,页面明显卡顿 2、上传慢:原图上传到 AI 服务耗时久,用户体验差 3、存储膨胀:IndexedDB 存太多大图很快触及限额

优化方案:

  • 上传前先压缩(Canvas 重绘,质量调至 0.7,最大边长限制 2048px)
  • 生成缩略图用于列表展示,点击后才加载原图
  • 使用 Web Worker 做压缩处理,避免阻塞主线程
javascript
// 压缩示例(简化版)
async function compressImage(file, maxWidth = 2048, quality = 0.7) {
  const bitmap = await createImageBitmap(file)
  const scale = Math.min(1, maxWidth / bitmap.width)
  const canvas = new OffscreenCanvas(bitmap.width * scale, bitmap.height * scale)
  const ctx = canvas.getContext('2d')
  ctx.drawImage(bitmap, 0, 0, canvas.width, canvas.height)
  return canvas.convertToBlob({ type: 'image/jpeg', quality })
}

麻烦五:复习功能的复杂性

错题本不只是记录,更重要的是复习。这块逻辑比想象中复杂:

艾宾浩斯遗忘曲线

按照遗忘曲线安排复习时间:

  • 第 1 次复习:学习后 20 分钟
  • 第 2 次复习:1 天后
  • 第 3 次复习:2 天后
  • 第 4 次复习:4 天后
  • 第 5 次复习:7 天后
  • 第 6 次复习:15 天后

每道题需要独立追踪复习状态和时间节点,状态机设计如下:

新录入 → 待复习(第1次) → 已掌握 / 待复习(第2次) → ... → 已归档

            答错 → 回退到上一轮

复习模式交互

复习时需要遮住答案让用户作答,答完后再显示正确答案对比。涉及:

  • 答案区域的显示/隐藏切换
  • 用户作答结果的记录(正确/错误/跳过)
  • 错误题目的自动回退重练
  • 复习统计数据的实时更新

这块前后端状态同步容易出 bug,特别是用户快速连续操作时。

总结

一个看起来简单的错题本工具,实际开发中遇到了这些主要挑战:

问题领域核心难点解决思路
题目识别手写体 + 公式 + 图文混排OCR + 多模态 AI 分级策略
公式渲染LaTeX 规范性差 + 移动端适配预处理清洗 + 容错降级
数据存储隐私 vs 同步 vs 成本本地优先 + 可选云同步
图片性能大图处理导致卡顿前端压缩 + 缩略图 + Web Worker
复习算法遗忘曲线 + 状态管理状态机设计 + 防抖节流

每个问题单独看都不算太难,但组合在一起就让开发周期远超预期。好在一步步踩坑过来,工具总算能用了。


工欲善其事,必先利其器。好的工具能让开发工作事半功倍。

如果你有其他好用的开发工具推荐,欢迎在评论区分享!

Released under the MIT License.