行业资讯

【中国方言题库|05】HarmonyOS ArkTS 东北话分库实战:让地区内容与通用列表解耦

发布时间:2026/8/22 15:59:00
【中国方言题库|05】HarmonyOS ArkTS 东北话分库实战:让地区内容与通用列表解耦 摘要地区题库很容易走向两种极端所有地区挤在一个巨型页面里或者每个地区复制一套列表、进度和跳转代码。“中国方言题库”的东北话分库采用第三种方式NortheastBankPage只固定b_northeast共享的BankDetailContent负责详情、章节、进度、多设备布局和练习入口MockBanks再通过题库 ID 选择本地内容。本文基于当前 HarmonyOS 5.0 ArkTS 源码拆解这条真实链路并讨论数据边界、章节分配、缓存、进度隔离和后续模块化方式。一、先看结论分库页面不等于复制页面当前NortheastBankPage.ets只有一个职责import { BankDetailContent } from ./BankDetailPage Entry Component struct NortheastBankPage { build() { Column() { BankDetailContent({ fixedBankId: b_northeast }) } .width(100%) .height(100%) } }它没有自己渲染题库封面、章节列表、进度条或底部按钮也没有自行读取东北话题目。它只是把地区身份交给共享组件。本文的唯一标记是地区入口只负责选择题库不复制通用列表。这句话也是判断分库架构是否健康的最简标准。二、为什么不直接复用一个带路由参数的详情页项目中原本就有通用BankDetailPage可以从路由参数读取bankId。东北话为何还要保留独立入口因为两类入口的产品语义不同通用详情页适合从首页列表、搜索结果等动态位置进入。NortheastBankPage代表一个稳定的地区目的地。固定入口便于路由表、快捷入口和后续地区专题复用。共享组件仍然只有一份不产生六套 UI。独立路由与共享实现并不矛盾。前者稳定导航语义后者控制维护成本。三、fixedBankId优先级隔离了外部参数BankDetailContent的初始化逻辑是aboutToAppear(): void { if (this.fixedBankId.length 0) { this.bank getBankById(this.fixedBankId) return } const params router.getParams() as BankDetailParams | undefined if (params params.bankId) { this.bank getBankById(params.bankId) } }这里的return很重要。东北话固定入口不会再被路由参数中的其他bankId覆盖。即使调用者错误地携带b_yue页面仍按b_northeast查找。这形成一个明确契约地区专属入口fixedBankId 优先 通用详情入口router params 生效如果把两个来源简单地用“后写覆盖前写”合并专题入口就可能展示另一个地区的题库封面、章节和进度也会一起串区。四、三种 ID 必须各司其职东北话相关数据至少有三种标识regionId: northeast bankId: b_northeast chapterId: northeast_c1 // 到 northeast_c6regionId用于地区目录bankId是题库查询和练习进度的主键chapterId用于章节过滤与章节进度。它们不能凭字符串相似就混用。一个安全的路由参数模型可以写成interface PracticeParams { bankId: string chapterId?: string mode: chapter | random | exam }当前代码使用字符串字段运行链路真实可用若后续重构联合类型能在编译期减少拼写错误但这属于扩展建议不是对现状的虚构。五、题库目录只保存元数据东北话题库目录项为{ id: b_northeast, regionId: northeast, name: 东北话题库, cover: $r(app.media.img_bank_cover_northeast), totalCount: 0, accuracy: 0, hot: 92, chapters: NE_CHAPTERS }它保存身份、展示信息和章节引用没有把所有题目嵌在页面结构里。真实题量会在目录同步时根据题目集合更新详情页因此可以读取统一的Bank模型。这种分离的价值不是“文件更好看”而是避免 UI 层知道题目由多少个数据文件拼接而来。详情页只问这是哪个题库、有多少题、有哪些章节。六、地区内容通过RAW_MAP选择东北话原始题目来自多组数组再按同一题库 ID 合并[ b_northeast, (((((NE_QUESTIONS .concat(NE_MORE_QUESTIONS) as RawQuestion[]) .concat(NE_EXTRA as RawQuestion[])) .concat(NE_EXTRA2 as RawQuestion[])) .concat(NE_EXTRA3 as RawQuestion[])) .concat(NE_EXTRA4 as RawQuestion[])) .concat(NE_EXTRA5 as RawQuestion[]) ]这里已经实现“地区内容与通用列表解耦”共享列表不知道NE_EXTRA5东北题目文件也不知道 ArkUI 卡片如何布局。两者只在b_northeast这个边界上会合。实际题目包含“嘎哈呢”“老铁”“埋汰”“贼拉”“咋整”“麻溜儿”等词汇也包含火炕、冰雪活动、二人转、酸菜等文化语境。文章只描述源码中能找到的内容不扩写不存在的语音库、地图或社区功能。七、构建阶段统一完成重分类、去重和截取getQuestions()并不是简单返回原始数组export function getQuestions(bankId: string): Question[] { let questions QUESTIONS_CACHE.get(bankId) if (!questions) { const source RAW_MAP.get(bankId) || SICHUAN_QUESTIONS const reclassified source.map((r: RawQuestion) { if (r.type audio) { return { type: vocab, stem: r.stem, options: r.options, answer: r.answer, analysis: r.analysis, example: r.example, audioHint: r.audioHint } as RawQuestion } return r }) // 后续执行题干去重、数量截取和模型展开 } return questions }这段源码明确说明听音题题型已经下线历史audio数据被重分类为vocab。因此不能把东北话分库宣传成现成的“东北话听力训练”。audioHint即使保留也只是字符串字段不等于可核验音频资源。八、题干去重有用但不是完整内容审核当前去重规则是const key r.stem.trim() if (seen.has(key)) continue seen.add(key) unique.push(r)它可以删除完全相同、仅首尾空格不同的题干却识别不了这些情况“咋整”是什么意思 咋整是什么意思 东北话“咋整”对应普通话哪句工程上可以生成“仅用于比较”的规范化 keyfunction dedupKey(stem: string): string { return stem .normalize(NFKC) .replace(/[“”]/g, ) .replace(/\s/g, ) .trim() }但自动规则不能替代编辑审核。两个相似题干可能考查不同语境尤其“调侃”“不认同”“邀请”等语用差异不能只看词面删除。九、章节分配当前依赖题目顺序题目展开时采用chapterId: ${bankId.replace(b_, )}_c${(i % 6) 1}所以东北题目会循环落入northeast_c1到northeast_c6。对应章节标题为const NE_CHAPTERS makeChapters(northeast, [ 儿化音, 日常用语, 幽默俗语, 炕头文化, 小品高频词, 冬季生活 ])这种方式保证六章都能得到题目适合当前本地题库快速组织但它不保证语义归类。数组第七题会再次进入第一章并不意味着它一定考查儿化音。若要让章节标题与内容严格对应原始题目应显式携带chapterIdinterface RegionQuestionSource { id: string bankId: b_northeast chapterId: northeast_c1 | northeast_c2 | northeast_c3 | northeast_c4 | northeast_c5 | northeast_c6 type: string stem: string options: string[] answer: number analysis: string }这是后续演进方案。当前真实实现仍是按索引取模不能在文章中说已经完成人工章节标注。十、共享详情不是“通用到没有地区特色”BANK_DETAIL_PROFILES为东北话提供独立文案{ subtitle: 直给、热闹、带节奏的东北话练习页, intro: 东北话题库更强调口语氛围、夸张语气和辨义理解练起来会很有生活感和表演感。, cultureNote: 小品语感、冬季生活和东北家庭交流是这套题库的特色适合培养辨识度。, focusTags: [语气理解, 高频俚语, 幽默表达], sceneTags: [熟人唠嗑, 家庭聊天, 冬天日常] }共享组件通过profile()按bankId取配置。布局、颜色、进度逻辑保持统一地区导语和场景标签仍然独立。复用的是能力不是把所有地区文案抹平。当新增地区时应该增加目录项、题目集合和 Profile而不是复制一份BankDetailPage.ets再逐处改文字。十一、空态属于共享组件的责任题库查找失败时详情组件展示“未找到题库”if (this.bank undefined) { Column({ space: 12 }) { Image($r(app.media.img_empty_default)) Text(未找到题库) } }地区入口无需再写一次错误页。这样无论错误来自路由参数、目录缺失还是开发期 ID 拼写用户看到的反馈一致。不过固定入口的自动化测试仍应检查b_northeast一定存在。空态是兜底不应掩盖构建期配置错误。十二、三种练习入口共享同一个题库主键章节按钮传递{ bankId: this.bank.id, chapterId: chapter.id, mode: chapter }底部两个操作传递{ bankId: this.bank!.id, mode: random } { bankId: this.bank!.id, mode: exam }PracticePage根据参数选择题目if (params.chapterId) { this.questions getQuestionsByChapter(params.bankId, params.chapterId) } else { this.questions getQuestions(params.bankId) }考试模式再选取 20 题随机模式进行洗牌。三条路径都保留b_northeast因此不会从四川话或粤语集合中混入题目。十三、章节参数要做归属校验当前getQuestionsByChapter()的过滤条件同时使用bankId和chapterIdexport function getQuestionsByChapter( bankId: string, chapterId: string ): Question[] { return getQuestions(bankId).filter( (question: Question) question.chapterId chapterId ) }如果传入b_northeast yue_c1结果为空练习页随后会回退到整个东北题库。这个回退避免白屏但也可能把错误章节参数静默转成随机范围。更严格的方案是在进入练习前验证前缀function belongsToBank(bankId: string, chapterId: string): boolean { return chapterId.startsWith(${bankId.replace(b_, )}_c) }验证失败时应记录开发日志并返回详情页而不是让用户误以为自己进入了某一章。十四、进度按题库与章节分别聚合详情页读取题库进度const p UserDataManager.getProgress( this.progressList, this.bankId() )章节进度同时带两个键const cp UserDataManager.getChapterProgress( this.chapterProgressList, this.bankId(), chapter.id )练习完成后PracticePage用相同的bankId、chapterId更新。只要入口到结果页始终保留b_northeast东北话进度就不会写入其他地区。测试时不能只看总进度数字变化还要完成一章东北题目后返回其他题库确认后者的章节数值保持不变。十五、题目 ID 仍有顺序耦合当前题目 ID 由索引生成id: ${bankId}_q${i 1}它天然带题库前缀避免不同地区出现同名q1但在数组中间插入题目会改变后续 ID。收藏、错题和笔记如果按题目 ID 保存就可能指向另一道题。更稳妥的地区内容应使用编辑期固定 ID{ id: ne_vocab_ga_ha_001, bankId: b_northeast, chapterId: northeast_c2, stem: 东北话“嘎哈呢”是什么意思 }固定 ID 不应从题干哈希临时生成因为修正一个标点也会改变哈希。它应作为内容资产的一部分长期维护。十六、缓存边界与更新策略QUESTIONS_CACHE以bankId缓存展开后的题目const QUESTIONS_CACHE: Mapstring, Question[] new Mapstring, Question[]()对纯本地静态题库而言这可以避免每次进入详情或练习都重复完成拼接、重分类、去重和模型转换。缓存键是题库 ID也天然隔离东北话与其他地区。若未来支持在线更新题包必须增加版本与失效机制interface BankCacheEntry { bankId: string contentVersion: number questions: Question[] }更新成功后只清理对应地区缓存不能为一条东北题目更新而重建全部题库更新失败则继续使用已验证的本地版本。十七、多设备适配继续由共享详情承担详情组件在断点为lg且页面宽度不小于 700vp 时使用双栏private useWideLayout(): boolean { return this.currentBp lg this.pageWidth 700 }常规窗口是单列滚动宽屏左侧放封面、概览和地区介绍右侧放学习重点和章节列表。东北入口根容器只保持满宽满高不重复任何断点判断。底部按钮还使用系统导航区域避让private bottomSafePadding(): number { return Math.max( Sizes.BOTTOM_NAV_MIN_PADDING, this.getUIContext().px2vp(this.navigationIndicatorHeightPx) ) }这也是共享的直接收益修正一次平板布局或底部安全区所有地区分库同时得到修复。十八、内容质量不能只靠代码类型ArkTS 类型可以保证options是字符串数组、answer是索引却不能判断解释是否准确、词语是否带冒犯语境、地区差异是否被过度概括。东北话内容审核至少应覆盖词义是否注明褒义、贬义或熟人调侃语境。“老娘们儿”“瘪三”等表达是否给出使用风险。黑龙江、吉林、辽宁内部差异是否被错误说成完全一致。网络流行语是否被误标为东北方言独有。小品、二人转、冬季生活等文化题是否避免刻板化。题干、选项与解析是否真正对应。技术架构解决“放在哪里、如何加载”内容审核解决“能否这样教”。两者缺一不可。十九、可执行的测试矩阵地区身份NortheastBankPage 始终得到 b_northeast 错误路由参数不能覆盖 fixedBankId 目录中 b_northeast 可被 getBankById 找到 封面与标题均属于东北话题库数据隔离东北题目只来自 b_northeast 映射 随机练习不混入其他 bankId 章节题目的 chapterId 以 northeast_ 开头 去重后题量与目录展示一致进度与交互完成章节后题库进度增加 章节进度只更新对应 chapterId 随机练习不伪造章节完成度 考试结果仍携带 b_northeast 收藏、错题和笔记可回查原题布局与可访问性phone 单列内容全部可滚动 tablet 与 2in1 宽屏切为双栏 大字体下章节标题不遮挡按钮 底部操作不进入系统手势区域 空题库和无效 ID 有明确反馈二十、上架文案的真实边界可以准确说明东北话分库有独立路由入口。页面固定选择b_northeast。共用详情组件展示章节、进度、地区导语与练习入口。本地题库包含词汇、对话、文化、俗语等内容。支持章节练习、随机练习和模拟考试。详情布局适配常规与宽屏窗口。当前不应说明每道题都经过方言学专家审定除非有真实记录。已提供真人东北话音频。历史audioHint就是可播放录音。六个章节已经全部按语义人工标注。地区内容来自在线实时更新。所有东北地区表达完全一致。二十一、总结东北话分库的关键不是新增一个页面而是把地区身份、共享 UI、地区内容和学习状态分成清晰边界。NortheastBankPage固定b_northeastBankDetailContent统一详情、空态、进度、多设备布局和三类入口MockBanks按题库 ID 合并并转换东北话内容PracticePage再根据题库与章节参数选择题目。当前实现已经避免了为每个地区复制列表页面也保留了独立地区文案和本地数据。但源码仍存在可验证的演进空间章节按索引取模、题目 ID 依赖数组顺序、去重只做trim()历史听音题被重分类为词汇题。把这些事实讲清楚比包装不存在的能力更有工程价值。当新增更多地区时继续坚持“薄入口、稳定主键、共享能力、独立内容、可追踪进度”这五条原则才能让题库规模增长而页面复杂度不成倍增长。---AI 辅助声明本文由 AI 辅助整理所有现状结论均基于“中国方言题库”当前NortheastBankPage、BankDetailContent、MockBanks、PracticePage与本地东北话题目源码复核建议代码均明确标注为演进方案未虚构真人音频、人工章节标注或在线题库能力。