qwerty-learner 本地词库与练习记录:从 JSON 词库文件到 IndexedDB 索引的设计
【免费下载链接】qwerty-learner为键盘工作者设计的单词记忆与英语肌肉记忆锻炼软件 / Words learning and English muscle memory training software designed for keyboard workers项目地址: https://gitcode.com/GitHub_Trending/qw/qwerty-learner
qwerty-learner 是一款在键盘上练习英语肌肉记忆的单词训练工具。本文解析它的本地词库存储机制:200 多个 JSON 词库文件如何被加载与分片,IndexedDB 里的练习记录如何建索引,以及错误单词、打字速度的统计口径是怎么算出来的。
词库数据从哪来
词库 JSON 文件的统一结构
所有词库都以 JSON 形式放在 public/dicts/ 目录下,从小学到 GRE、雅思、考研,还有日语、德语、印尼语等多语言词库。每个词库就是一组单词数组,单词结构统一为四个字段:
name:单词trans:释义数组usphone/ukphone:美音、英式音标
以 CET4_T.json 为例,单词 "cancel" 就带着释义"取消,撤销;删去"和音标。正因为结构固定,任何一份文件丢进目录就能被程序直接消费,这也是文档里鼓励贡献词库的原因。
词库选择界面,本地 JSON 词库按级别分类展示
词库如何被加载与分片
前端通过 wordListFetcher 用fetch把词库 JSON 拉回来并解析成单词数组,再交给 SWR 做内存缓存——同一个词库切换页面不会重复请求。大词库按"章节"切分,练习界面一次只加载当前章节,配合章节组件做懒加载,避免一次性把几万个单词铺进内存。由于词库文件与页面同源部署,断网后浏览器也能直接读缓存,词库部分天然支持离线。
练习记录写入 IndexedDB 的方式
三张记录表和索引怎么建
词库本身不写数据库,写进 IndexedDB 的是"你打了什么"。项目用 Dexie(IndexedDB 的封装)在 src/utils/db 定义了名为RecordDB的库,包含三张表:
wordRecords:每个单词的一次练习记录chapterRecords:每轮章节练习的汇总reviewRecords:错题复习的进度
表的索引定义只有一行,却决定了后面所有查询的快慢:
this.version(2).stores({ wordRecords: '++id,word,timeStamp,dict,chapter,wrongCount,[dict+chapter]', chapterRecords: '++id,timeStamp,dict,chapter,time,[dict+chapter]', })每个单词、词库 ID、时间戳、错误次数都单独建索引,另外加了[dict+chapter]复合索引。查询"某词库某章节的全部记录"走复合索引,查询"某时间段的全部记录"走timeStamp范围查询,查询"打错过几次的单词"走wrongCount索引。索引是提前为最常用查询准备的。
一条单词记录里存了什么
每次打完一个单词,WordRecord 会记下:
word与dict、chapter:哪个词库的哪个词timing:正确输入时每个字母的耗时差数组,加起来就是总用时wrongCount:出错的次数(输错会清空重打,但只记一次错误)mistakes:第几个字母位置被错打成了哪个键
mistakes这个字段是后面"哪个按键最容易按错"分析的数据来源。章节级的ChapterRecord则额外保存正确/错误按键总数和未出错的单词下标,打字速度(wpm)、按键正确率、单词正确率都写成 getter 现算,不落库。
练习完成后,逐词记录与章节汇总分别写入 wordRecords 和 chapterRecords
统计与错误单词是怎么算出来的
错误单词的统计口径
错题页的 useErrorWords 先按索引取出"这个词库里错过的单词":
db.wordRecords .where('wrongCount') .above(0) .filter((record) => record.dict === dict.id) .toArray()wrongCount索引先过滤掉所有零错误的记录,再在内存里按word分组累加。每个错误单词最终得出四个指标:累计错误次数errorCount、按字母位置统计的易错点errorLetters、最近一次出错时间latestErrorTime,以及把这两个维度排名后按 0.6 / 0.4 加权得到的复习顺序——错误越多、错得越近的单词排在越前面,复习列表就按这个顺序生成并写入reviewRecords。
速度、正确率与热力图的口径
分析页的 useWordStats 用timeStamp索引做区间查询,把一段时间的记录按天聚合:
- 练习次数、去重后单词数 → 生成活动热力图
- wpm = 当日练习词数(不去重)÷ 总用时 × 60
- 正确率 = 单词字母总长 ÷(字母总长 + 错误次数)× 100%
- 按错的键归并成大写字母 → 输出"哪个键最常按错"的柱状图
每日 wpm 折线图,数据来自 wordRecords 中 timing 字段的按天聚合
边界与取舍
为什么全放本地而不是上服务器
学习记录(打了什么、错在哪)本质是个人数据,放在浏览器本地 IndexedDB 里意味着隐私不出机器、离线可用、零后端成本。代价是没有跨设备同步,项目给出的解法是手动迁移:data-export 把整个库导出、gzip 压缩成Qwerty-Learner-User-Data-日期.gz文件,导入时解压回灌,换设备靠这一个文件。
数据库版本演进与什么不入库
RecordDB从 version 1 一路升到 version 3,字段从errorCount改名到wrongCount、新增了reviewRecords表,Dexie 的 version 机制负责平滑迁移老用户的数据。另一条边界值得注意:词库 JSON 永远不进 IndexedDB,库里只存"练习行为"。这样做的好处是词库更新(比如新加了释义)不需要迁移用户数据,代价是错题页要展示释义时,仍需用词库文件把word名字反查回完整词条。整体来看,这套"静态词库走文件 + 动态记录走 IndexedDB + 文件导入导出兜底"的组合,是一个纯前端项目在隐私、离线和复杂度之间比较克制的选择。
这套解析对想给前端小工具加本地持久层的开发者有参考价值:先用统一 JSON 结构组织静态数据,再按"最常用查询"设计 IndexedDB 索引,最后用文件导入导出替代账号同步,三步就能搭出一个离线可用的本地词库与练习记录系统。
【免费下载链接】qwerty-learner为键盘工作者设计的单词记忆与英语肌肉记忆锻炼软件 / Words learning and English muscle memory training software designed for keyboard workers项目地址: https://gitcode.com/GitHub_Trending/qw/qwerty-learner
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考