【中国方言题库|03】HarmonyOS ArkTS 四川话分库实战:复用题库组件并保持地区参数清晰

摘要:地区分库页面看起来简单,却承担着稳定身份、路由可达和共享组件复用三项关键职责。本文基于"中国方言题库"真实源码中的 SichuanBankPage.ets,沿着 BankCard -> SichuanBankPage -> BankDetailContent -> PracticePage 追踪 b_sichuan,说明如何用薄入口组件复用题库详情、学习进度、章节列表和多设备布局,同时避免复制业务代码、路由参数污染与地区数据串库。

一、只有十几行的页面为什么值得单独分析

SichuanBankPage.ets 的全部核心代码非常短:

ts 复制代码
import { BankDetailContent } from './BankDetailPage'

@Entry
@Component
struct SichuanBankPage {
  build() {
    Column() {
      BankDetailContent({ fixedBankId: 'b_sichuan' })
    }
    .width('100%')
    .height('100%')
  }
}

它没有自己加载题目、计算进度、渲染章节,也没有再次实现随机练习和模拟考试。页面只做两件事:

  1. 作为 ArkUI 路由入口存在。
  2. 把稳定地区 ID b_sichuan 注入共享详情组件。

这种"薄入口"不是代码不足,而是有意保持职责单一。本文的唯一标记是:地区入口只负责固定题库身份 。所有可复用业务都留在 BankDetailContent,四川话差异来自目录和文化档案,而不是复制一份详情页后再修改文案。

二、完整调用链从题库卡片开始

首页和题库列表使用 BankCard。卡片根据 bank.id 决定目标页:

ts 复制代码
private detailPageUrl(): string {
  switch (this.bank.id) {
    case 'b_sichuan':
      return 'pages/SichuanBankPage'
    case 'b_yue':
      return 'pages/YueBankPage'
    case 'b_northeast':
      return 'pages/NortheastBankPage'
    case 'b_shanghai':
      return 'pages/ShanghaiBankPage'
    case 'b_minnan':
      return 'pages/MinnanBankPage'
    case 'b_hakka':
      return 'pages/HakkaBankPage'
    default:
      return 'pages/BankDetailPage'
  }
}

点击四川话卡片时:

ts 复制代码
router.pushUrl({
  url: this.detailPageUrl(),
  params: { bankId: this.bank.id }
})

虽然路由仍携带 bankId,四川话固定入口不会依赖它。BankDetailContent.aboutToAppear() 先检查 fixedBankId

ts 复制代码
if (this.fixedBankId.length > 0) {
  this.bank = getBankById(this.fixedBankId)
  return
}

只有未传固定 ID 时,通用 BankDetailPage 才读取 Router 参数。这条优先级保证即使页面栈中残留了其他地区参数,SichuanBankPage 仍只能展示四川话题库。

三、固定参数比页面内部常量更容易复用

一种常见写法是在详情组件内部判断当前页面名称,或在每个地区页面复制一套 UI。当前工程选择属性注入:

ts 复制代码
BankDetailContent({ fixedBankId: 'b_sichuan' })

优点包括:

  • 详情组件可以独立测试不同 fixedBankId
  • 地区入口无需知道详情实现。
  • 通用详情页仍可支持动态 bankId
  • 六个入口结构保持一致。
  • 地区身份在组件创建处一眼可见。

如果把四川 ID 隐藏在全局变量或 AppStorage 中,页面恢复时容易读到上一个地区;如果把它写进 BankDetailContent 内部,又会破坏组件复用。属性传递是最窄、最明确的依赖。

四、六个地区入口保持同构

工程中还存在:

text 复制代码
YueBankPage          -> b_yue
NortheastBankPage    -> b_northeast
ShanghaiBankPage     -> b_shanghai
MinnanBankPage       -> b_minnan
HakkaBankPage        -> b_hakka

每个页面都采用相同根布局与共享组件。这样做能让 AppGallery 的多设备适配、系统返回和稳定性修复一次覆盖六个分库。

同构不等于可以手工随意复制。复制入口文件后最常见的错误是文件名改了,fixedBankId 没改,导致"粤语题库"入口打开四川话内容。建议为入口建立表驱动测试:

页面 预期 ID 预期名称
SichuanBankPage b_sichuan 四川话题库
YueBankPage b_yue 粤语题库
NortheastBankPage b_northeast 东北话题库
ShanghaiBankPage b_shanghai 上海话题库
MinnanBankPage b_minnan 闽南语题库
HakkaBankPage b_hakka 客家话题库

五、路由清单是入口可达性的第二个真源

只有 @Entry 还不够,页面必须注册在 main_pages.json

json 复制代码
{
  "src": [
    "pages/BankDetailPage",
    "pages/SichuanBankPage",
    "pages/YueBankPage",
    "pages/NortheastBankPage",
    "pages/ShanghaiBankPage",
    "pages/MinnanBankPage",
    "pages/HakkaBankPage"
  ]
}

页面文件、路由字符串和清单路径必须完全一致。重命名文件后只改 import,不改清单,编译可能通过部分阶段,但运行跳转会失败。发布前应自动检查:

text 复制代码
BankCard 返回的每个 pages/... 路径
必须存在于 main_pages.json
且对应 .ets 文件真实存在

这是很适合写成静态脚本的规则,不需要每次靠人工逐个点击才能发现拼写错误。

六、题库目录中 b_sichuan 的真实内容

MockBanks.ets 将四川话地区与题库分开建模:

ts 复制代码
{ id: 'sichuan', name: '四川话', shortName: '川', cover: ... }

{
  id: 'b_sichuan',
  regionId: 'sichuan',
  name: '四川话题库',
  totalCount: 0,
  accuracy: 0,
  hot: 98,
  chapters: SICHUAN_CHAPTERS
}

地区 ID 是 sichuan,题库 ID 是 b_sichuan。入口传递的是题库 ID,因为详情页需要查找章节、题目与学习进度。混用两个 ID 会导致 getBankById() 返回 undefined,页面进入"未找到题库"空态。

四川话题库包含六个章节:

text 复制代码
基础词汇
日常生活
人称称谓
饮食词汇
俗语歇后语
麻将与娱乐

题量并非永久写死在 Bank 模型中。目录初始化时从去重后的真实题目集合回填 totalCount 和章节总量。分库页显示的"共 N 题"因此应与练习页可用题目保持一致。

七、四川话文化差异来自 Profile,而不是页面分支

共享详情组件通过 BANK_DETAIL_PROFILES 查找四川话档案:

ts 复制代码
['b_sichuan', {
  subtitle: '巴适表达、茶馆闲谈和川味生活都在这里',
  intro: '四川话题库偏重高频口语、称呼调侃和饮食表达,适合先从有画面感的词句入门。',
  cultureNote: '会结合茶馆、火锅、麻将社交等语境,帮助理解"摆龙门阵""巴适"这类代表性说法。',
  focusTags: ['高频口头词', '饮食表达', '俗语语感'],
  sceneTags: ['茶馆聊天', '火锅点单', '朋友调侃']
}]

页面结构仍然是 Hero、学习进度、简介、文化提示、重点标签和章节列表。只替换档案数据,就能形成地区识别度。

文化内容需要人工校对。比如熟人之间的调侃不一定适合正式场合,同一表达在四川不同地区也可能有差异。代码能保证字段存在,却不能自动证明文化解释具有普遍性。内容维护应记录适用语境和审核来源。

八、学习进度通过题库 ID隔离

共享组件使用:

ts 复制代码
UserDataManager.getProgress(this.progressList, this.bankId())

章节进度使用题库 ID 与章节 ID 联合查询:

ts 复制代码
UserDataManager.getChapterProgress(
  this.chapterProgressList,
  this.bankId(),
  chapter.id
)

这意味着 b_sichuan 不只是展示参数,也是本地学习状态的分区键。若写入时使用 sichuan,读取时使用 b_sichuan,用户完成练习后详情页仍会显示 0。

建议把 ID 类型收紧,减少任意字符串:

ts 复制代码
export type BankId =
  | 'b_sichuan'
  | 'b_yue'
  | 'b_northeast'
  | 'b_shanghai'
  | 'b_minnan'
  | 'b_hakka'

ArkTS 工程也可以用常量对象统一引用,避免入口、目录、Profile 和测试中重复手写字符串。

九、共享详情保留多设备能力

四川话入口没有自己的断点判断,但它复用了 BankDetailContent 的适配能力。当 currentBp === 'lg' 且页面宽度不小于 700vp 时,详情页使用双栏:

  • 左栏:封面、学习摘要、题库简介。
  • 右栏:学习重点、常见语境、章节练习。

其他窗口使用单列滚动。底部随机练习与模拟考试按钮根据系统导航指示区计算安全距离。

这正是复用的价值:如果六个地区各自实现详情页,任何底部安全区、横屏或 2in1 修复都要改六次。现在只需修正共享组件,固定入口自然获得同一行为。

十、薄入口仍需要稳定根容器

SichuanBankPage 外层使用:

ts 复制代码
Column() {
  BankDetailContent({ fixedBankId: 'b_sichuan' })
}
.width('100%')
.height('100%')

根容器满宽满高,确保内部详情可以正确计算 onAreaChange 和滚动区域。若只依赖子组件自然尺寸,宽屏断点可能得到错误宽度,底部操作栏也可能无法贴合页面底部。

入口没有额外背景和 padding,避免与共享组件的背景、TopBar 和安全区重复。薄入口应尽量透明,不在外层再增加一层卡片或滚动容器。

十一、从分库进入三种练习模式

在共享详情中,四川话题库可以进入:

ts 复制代码
// 章节练习
{ bankId: 'b_sichuan', chapterId, mode: 'chapter' }

// 随机练习
{ bankId: 'b_sichuan', mode: 'random' }

// 模拟考试
{ bankId: 'b_sichuan', mode: 'exam' }

参数链路中 bankId 必须保持不变。PracticePage 应先校验题库,再根据 mode 选择题目:

  • chapter 只使用对应章节题目。
  • random 从四川话题库真实题目中抽取。
  • exam 按当前可用题量组成试卷。

不能因为入口是四川话专属页面,就让 PracticePage 依赖"上一页是什么"。目标页只应信任明确参数,这样从搜索、收藏或恢复页面进入时仍能工作。

十二、为何不直接统一成一个动态详情路由

既然通用 BankDetailPage 能读取 bankId,为什么还保留六个固定入口?当前设计可能有以下工程考虑:

  • 每个地区需要稳定、可独立注册的页面地址。
  • 后续可能加入地区专属资源或入口配置。
  • 固定页面可防止路由参数被错误覆盖。
  • 页面统计或测试可以按地区入口分别执行。

代价是路由文件和清单条目增多。若未来地区数量从 6 增长到 50,继续为每个地区创建页面会产生维护负担。那时可以改为一个类型安全的动态详情页,并在 Navigation 中统一路由契约。

当前只有六个地区,薄入口模式仍然简单可控。不要因为追求"零重复"过早引入复杂路由框架。

十三、switch 映射可以进一步集中

BankCard.detailPageUrl() 当前用 switch 将题库 ID 映射到页面。入口文件和清单新增后,还要记得修改 switch。可以用结构化 Map 减少分散配置:

ts 复制代码
const BANK_DETAIL_ROUTES: Map<string, string> = new Map([
  ['b_sichuan', 'pages/SichuanBankPage'],
  ['b_yue', 'pages/YueBankPage'],
  ['b_northeast', 'pages/NortheastBankPage'],
  ['b_shanghai', 'pages/ShanghaiBankPage'],
  ['b_minnan', 'pages/MinnanBankPage'],
  ['b_hakka', 'pages/HakkaBankPage']
])

function detailRoute(bankId: string): string {
  return BANK_DETAIL_ROUTES.get(bankId) || 'pages/BankDetailPage'
}

更进一步,可以把 route 加入 Bank 元数据,但要避免让纯业务模型过度依赖页面路径。若目录模型还要在非 UI 模块复用,路由映射留在导航层更合适。

十四、空态不是"不会发生"

即使固定 ID 写在源码中,仍可能因为目录重命名、题库移除或初始化失败而找不到。共享详情会显示"未找到题库",不会崩溃。

对于固定入口,这种情况属于工程配置错误,不只是普通空数据。调试构建可以记录:

text 复制代码
SichuanBankPage fixedBankId=b_sichuan
getBankById result=undefined

发布构建不应展示内部路径或堆栈,但可以提供返回题库列表的操作。自动化测试应让所有固定 ID逐一通过 getBankById(),在构建前发现配置断裂。

十五、入口测试比截图更重要

四川话分库至少需要以下测试:

身份测试

  • 打开 SichuanBankPage 后标题为"四川话题库"。
  • Hero 封面来自四川话资源。
  • 文化档案包含四川话语境,而非其他地区。
  • 六个章节名称与四川目录一致。

参数测试

  • 路由传入 b_yue 时,固定入口仍展示 b_sichuan
  • 通用 BankDetailPage 传 b_sichuan 时得到相同详情内容。
  • 不存在的固定 ID进入空态。
  • 练习页收到的 bankId 始终为 b_sichuan

状态测试

  • 完成四川话题目后,只更新四川话进度。
  • 粤语进度不会出现在四川话页。
  • 完成章节后按钮与进度立即更新。
  • 重启应用后本地进度可恢复。

适配测试

  • 手机单列与底部操作正常。
  • 平板和 2in1 双栏切换正常。
  • 大字体下标签和章节标题不重叠。
  • 系统导航区不会遮挡练习按钮。

十六、参数串库的典型症状和定位顺序

若用户点击四川话却看到粤语内容,按以下顺序排查:

  1. BankCardbank.id 是否为 b_sichuan
  2. detailPageUrl() 是否返回 pages/SichuanBankPage
  3. main_pages.json 是否注册正确页面。
  4. SichuanBankPage 是否注入 b_sichuan
  5. getBankById('b_sichuan') 是否返回四川题库。
  6. BANK_DETAIL_PROFILES 的 key 是否为同一 ID。
  7. 本地进度写入是否也使用 b_sichuan
  8. PracticePage 是否继续沿用传入 ID。

不要一开始就怀疑 ArkUI 状态刷新。先沿 ID 链逐层核对,通常能在很小范围内找到错配。

十七、薄入口的性能成本很低

SichuanBankPage 本身不复制数据,也不创建额外服务,只实例化共享组件并传递字符串属性。运行时主要成本仍来自详情页的图片、列表、进度计算和滚动布局。

性能优化应落在共享层:

  • 题库查询使用稳定索引。
  • 章节进度按 ID 快速查找。
  • 封面提供适合展示尺寸的资源。
  • 状态变化时避免反复遍历大量历史。
  • 宽屏两栏不重复构建同一内容。

不要为了优化十几行入口而引入全局缓存;那不会解决真正的渲染成本。

十八、上架材料中的真实能力边界

根据当前源码,可以准确描述:

  • 四川话拥有独立可达的题库入口。
  • 入口复用统一详情组件。
  • 地区身份固定为 b_sichuan
  • 页面展示四川话简介、文化提示、重点、语境和章节。
  • 学习进度来自本地记录。
  • 支持章节、随机和考试三种练习入口。
  • 共享详情适配 phone、tablet、2in1。

不能因为有独立页面就声称四川话分库拥有独立网络服务、独立账号或独立云题库。当前差异来自包内目录、题目和文化档案。

十九、可复用的地区入口模板

新增一个地区时,最小步骤是:

text 复制代码
1. 在题库目录定义唯一 Bank ID
2. 添加题目和章节并同步真实数量
3. 增加文化 Profile
4. 创建薄入口并注入固定 ID
5. 注册 main_pages.json
6. 在导航映射中增加路径
7. 验证详情、进度和三种练习模式
8. 完成 phone、tablet、2in1 适配回归

每一步都围绕同一 ID。只要 ID 在目录、Profile、入口、路由、持久化和练习页保持一致,地区扩展就不会演变成六套独立业务。

二十、总结

四川话分库最值得复用的不是一段复杂算法,而是一条清晰边界:入口固定身份,共享组件承担详情业务,目录和文化档案提供差异,练习页只依赖显式参数。SichuanBankPage 虽然只有十几行,却把地区身份稳定地接入了完整学习链路。

对于 HarmonyOS 5.0 以上的 ArkUI 工程,薄入口模式尤其适合"结构相同、内容不同"的模块。它减少重复,保留独立路由,又让多设备适配、底部安全区、进度展示和异常空态可以集中修复。真正需要严格维护的不是页面代码量,而是 b_sichuan 这条参数链在所有层级中始终一致。


AI 辅助声明: 本文由 AI 辅助整理,所有结论基于中国方言题库当前 SichuanBankPage、共享详情组件、题库目录、路由清单和练习参数真实源码复核;未将薄入口描述为独立业务或云端能力。

相关推荐
贾伟康1 小时前
【中国方言题库|10】HarmonyOS ArkTS 语音播放实战:管理读音播放与页面生命周期
生命周期·harmonyos·arkts·语音合成·texttospeech
YM52e12 小时前
鸿蒙ArkTS实战项目 - 门店陈列巡检台:巡检卡片与多列切换实现
学习·华为·harmonyos
lilian23313 小时前
Harmony os 技术实战|拼豆制图27:用单字符编码承载 50 张 70×70 图纸
前端·数据库·华为·harmonyos
HwJack2018 小时前
UIAbility 生命周期全链路:从冷启动到热启动的实战笔记
笔记·华为·harmonyos
梦想不只是梦与想19 小时前
鸿蒙 AppGallery Connect:应用创建(二)
harmonyos·appgallery·创建应用
●VON21 小时前
芯笺 Markdown:面向 HarmonyOS PC 的本地优先 Markdown 编辑器
华为·编辑器·harmonyos·鸿蒙
世人万千丶21 小时前
鸿蒙项目实战 - 社区活动编排板:标签云布局算法与自动换行
学习·算法·华为·harmonyos·鸿蒙
YM52e1 天前
鸿蒙ArkTS项目实战 - 门店陈列巡检台:完整代码与运行效果
学习·华为·harmonyos
贾伟康1 天前
【中国方言题库|06】HarmonyOS ArkTS 闽南语与客家话实战:统一分库页面导航与空状态
harmonyos·arkts·arkui·router·空状态