HarmonyOS APP<玩转React>开源教程二十三:面试题库功能
第23次:面试题库功能
本文讲解题库系统的主干实现。它对应的不是单个文件,而是一条完整链路:
InterviewQuizService.ets、QuizBankPage.ets、QuizPracticePage.ets,以及与错题本的联动。阅读时请把它当作一个完整的刷题系统来理解。
题库系统在项目里的真实定位
课程内测验强调“跟课学完顺手测一下”,而面试题库强调的是“体系化刷题”。因此它的设计比每日一题和普通课程测验都更完整:
- 有模块分类
- 有统计卡片
- 有模块练习
- 有错题本入口
- 有练习总结页
先看整条链路:
一、为什么题库首页要先显示统计再显示模块
QuizBankPage 的内容顺序非常有讲究:
- 顶部导航
- 我的统计卡片
- 错题本入口
- 模块题库列表
这个顺序不是为了好看,而是为了建立学习反馈感。用户进入题库首页时,先看到的是:
- 我一共答了多少题
- 我答对了多少题
- 我的正确率是多少
然后再看到模块列表,才更容易知道下一步去哪里练。这个设计非常符合学习产品的逻辑,因为用户不是来“浏览信息”的,而是来“持续进步”的。
二、InterviewQuizService 的职责比你想象的大
当前题库服务做的事情非常完整:
- 初始化全部题目
- 读取整体统计数据
- 记录每题答题结果
- 维护模块级统计
- 维护整体正确率
- 记录每道题最后一次答题状态
- 支持重置统计
这里最值得学习的是它不仅存“总答题数”,还存“每个模块的统计”。这意味着用户后续可以看到自己是 Hooks 模块弱,还是组件模块弱,而不是只知道一个笼统的全局正确率。
这类“全局统计 + 局部统计”并存的设计,在题库类应用中非常常见,也非常实用。
三、答题记录为什么不是简单累加
recordAnswer(questionId, answer, isCorrect) 是这套系统最关键的方法之一。它的设计并不是“每答一次就无脑加一”,而是更细致地处理了同一道题的重复作答:
- 第一次作答:计入总答题数和正确/错误数。
- 如果之前答错,后来答对:要修正统计。
- 如果之前答过且结果没变:不重复累计。
这说明当前统计系统更接近“题目掌握情况”,而不是“机械刷题次数”。这种设计对学习产品更友好,因为它鼓励用户修正错误,而不是把统计做得越来越失真。
四、题库首页为什么还要初始化错题本服务
QuizBankPage 在加载时不仅调用 InterviewQuizService.init(),还调用 WrongAnswerService.init()。这是因为题库首页需要同时展示:
- 我的答题统计
- 错题本入口
- 当前未掌握错题数量
也就是说,题库首页本质上已经是“练习中心”,而不是单纯的目录页。它既展示当前能力,也展示后续复习入口。
这一层入口在 QuizBankPage.ets 里对应的是下面这段真实加载逻辑:
private async loadData(): Promise<void> {
this.isLoading = true;
await InterviewQuizService.init();
await WrongAnswerService.init();
this.statistics = InterviewQuizService.getOverallStatistics();
this.wrongAnswerCount = WrongAnswerService.getUnmasteredCount();
const moduleList = InterviewQuizService.getAllModules();
const tempModules: ModuleInfo[] = [];
for (const m of moduleList) {
const questions = InterviewQuizService.getQuestionsByModule(m.moduleId);
const moduleInfo: ModuleInfo = {
moduleId: m.moduleId,
moduleName: m.moduleName,
icon: m.icon,
questionCount: questions.length,
stats: InterviewQuizService.getModuleStatistics(m.moduleId)
};
tempModules.push(moduleInfo);
}
this.modules = tempModules;
this.isLoading = false;
}
五、模块列表为什么要显示每个模块的正确率
当前 ModuleItem 中显示了:
- 模块图标
- 模块名称
- 题目总数
- 当前模块正确率
这不是简单的信息堆砌,而是给用户非常明确的练习反馈:
- 题量决定刷题成本
- 正确率反映掌握程度
当用户看到某个模块题目不多但正确率很低时,就知道那里是弱项;如果某个模块正确率已经很高,可能就可以暂时放一放。这就是“数据驱动学习路径”的基本思路。
六、QuizPracticePage 为什么支持两种模式
这一页非常值得学,因为它没有被写成“只能练模块题”。
当前它支持两种进入方式:
- 通过
moduleId进入模块练习模式 - 通过
isFromWrongBook进入错题练习模式
这种设计很聪明,因为:
- 页面 UI 基本一样
- 差异主要体现在题目来源
- 复用一个页面就能覆盖两种练习场景
也就是说,页面层的复用不是靠硬拷贝,而是靠“参数化驱动不同模式”。
七、练习页里的答题闭环是怎样形成的
QuizPracticePage 的答题流程非常完整:
- 加载题目并随机打乱顺序。
- 展示当前题目和选项。
- 用户选择答案。
- 提交后立即判断对错。
- 调用
InterviewQuizService.recordAnswer()更新统计。 - 如果答错,调用
WrongAnswerService.addWrongAnswer()。 - 如果来自错题本且答对,调用
WrongAnswerService.markAsMastered()。 - 最后一题做完后显示总结页。
这说明题库系统真正形成了:
- 练习
- 统计
- 错题
- 复习
四段闭环,而不是只有“答题”这一个动作。
把它放回真实源码里看,会更容易理解这条链路是如何闭合的:
private async handleSubmit(): Promise<void> {
const question = this.getCurrentQuestion();
if (!question || this.selectedAnswer < 0) return;
this.isCorrect = InterviewQuizService.validateAnswer(question.id, this.selectedAnswer);
this.isSubmitted = true;
this.answers.push(this.selectedAnswer);
this.results.push(this.isCorrect);
await InterviewQuizService.recordAnswer(question.id, this.selectedAnswer, this.isCorrect);
if (!this.isCorrect) {
await WrongAnswerService.addWrongAnswer(question, this.selectedAnswer);
} else if (this.isFromWrongBook) {
await WrongAnswerService.markAsMastered(question.id);
}
}
注意这里除了更新统计,还把用户本次选择和结果顺手记录进了 answers、results,这样页面后续才能继续切题并生成总结页。
八、总结页为什么非常重要
练习页最后的 SummaryView() 展示了:
- 总题数
- 正确题数
- 正确率
- 用时
这个总结页的意义在于,它给用户一次完整练习画上了一个可感知的句号。没有总结页,用户只会觉得“题做完了”;有总结页,用户会觉得“我完成了一次训练”。
对于学习产品来说,这种仪式感其实很重要。它能帮助用户形成持续刷题的动力。
九、自己实操时最推荐的顺序
- 打开题库首页,先看统计卡片和模块列表。
- 进入任意模块做几道题。
- 故意答错一两道,观察结果卡片和解析。
- 完成全部题目后看总结页。
- 回到题库首页,观察统计是否更新。
- 进入错题本,再从错题本发起练习,看答对后是否会被标记为已掌握。
这一套走完后,你对题库链路的理解会非常扎实。
十、本篇常见坑
1. 只做题目列表,不做统计
没有统计的题库很难形成成长反馈。
2. 同一道题重复作答时直接无脑累加
这样会让统计越来越失真。
3. 错题本和练习页分家过度
当前项目的优点恰恰在于两者通过参数模式复用了同一个练习页。
4. 练习完成后不做总结页
会大大削弱一次练习的完成感。
本篇小结
面试题库功能并不是一个单独页面,而是一整套学习闭环:
QuizBankPage负责入口和总览InterviewQuizService负责统计与题库规则QuizPracticePage负责实际练习WrongAnswerService负责错题沉淀与复习
如果你把这一套结构真正理解透了,后面看错题本功能就会非常自然,因为它本来就是题库系统的一部分。
课后练习
- 自己总结
QuizService和InterviewQuizService在业务目标上的区别。 - 画出“模块练习模式”和“错题练习模式”在
QuizPracticePage中的分流图。 - 思考如果你要增加“只练未做过题目”模式,最适合把过滤逻辑写在服务层还是页面层,为什么。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)