HarmonyOS 应用开发《掌上英语》第16篇:枚举与常量管理:RouterMap 的设计模式 枚举与常量管理RouterMap 的设计模式一、引言在大型应用中路由路径的管理是一件容易被忽视但影响深远的事情。如果每个页面跳转都直接使用字符串字面量一旦路径需要修改将面临全局搜索替换的噩梦——改漏一个就会导致页面跳转失败。我们英语学习 App 包含 40 个页面横跨 4 个 feature 模块路由管理必须做到集中、类型安全、易于维护。RouterMap枚举 RouterTable注册表 RouterModule封装的三层架构正是为此而设计。本文重点分析第一层——RouterMap枚举的设计模式。二、RouterMap字符串枚举的路由表2.1 完整定义// commons/commonLib/src/main/ets/constants/RouterMap.etsexportenumRouterMap{// 主页MAIN_PAGEMainPage,// 登录LOGIN_PAGELoginPage,// -----------------------------------------------------练习CHAPTER_PRACTICE_PAGEChapterPractice,MATERIAL_DOWNLOAD_PAGEMaterialDownload,FEATURE_COURSE_PAGEFeatureCourses,SEARCH_PAGESearchPage,SEARCH_INPUT_PAGESearchQuestionPage,// -----------------------------------------------------课程ANSWER_QUESTIONS_PAGEAnswerQuestionsPage,ANSWER_QUESTIONS_TWO_PAGEAnswerQuestionsTwoPage,ANSWER_REPORT_PAGETestReportPage,EXAM_RESULT_PAGEExamResultPage,GOOD_COURSE_DETAIL_PAGEGoodCourseDetailPage,MY_WRONG_PAGEMyWrongPage,MY_COLLECTION_PAGEMyCollectionPage,MY_NOTE_PAGEMyNotePage,View_NOTE_PAGEViewNotePage,Mock_PAGEMockTestPage,COURSE_INTRODUCTION_PAGECourseIntroductionPage,Level_TWO_PAGESecondListPage,Level_THREE_PAGEThirdListPage,Level_ONE_PAGETopicHomePage,// -----------------------------------------------------我的MESSAGE_CENTER_PAGEMessageCenterPage,SET_UP_PAGESetUpPage,FEEDBACK_PAGEFeedbackPage,FEEDBACK_RECORD_PAGEFeedbackRecordPage,PRIVACY_STATEMENT_PAGEPrivacyStatementPage,PRIVACY_AGREEMENT_PAGEPrivacyAgreementPage,PRIVACY_USE_PAGEPrivacyUseAlertPage,PERSONAL_CENTER_PAGEEditPersonalCenter,ABOUT_PAGEAboutPage,COURSE_PAGECoursePage,PRACTICE_RECORDS_PAGEPracticeRecordsPage,PRACTICE_DETAILS_PAGEPracticeDetailsPage,DAY_PRACTICE_DETAILS_PAGEOneDayPracticeRecordsPage,COLLECTION_PAGECollectionPage,LEARNING_PLAN_PAGELearningPlanSettingPage,REMINDER_SETTING_PAGEReminderSettingPage,MY_ORDER_PAGEMyOrderPage,Order_Detail_PAGEOrderDetailPage,BROWSING_HISTORY_PAGEBrowsingHistoryPage,DASHBOARD_PAGEDashboardPage,DAILY_CHALLENGE_PAGEDailyChallengePage,WORD_CARD_PAGEWordCardPage,SPEECH_EVAL_PAGESpeechEvalPage,}2.2 关键设计决策为什么用字符串枚举而非数字枚举ArkTS 的枚举默认是数字枚举MAIN_PAGE 0但 RouterMap 选择的是字符串枚举。原因有三可读性RouterMap.MAIN_PAGE的值是MainPage在调试、日志和序列化时一目了然。序列化友好路由路径通常需要在配置文件中使用如route_map.json字符串值天然支持 JSON 序列化。与 NavPathStack 兼容HarmonyOS 的NavPathStack.pushPathByName()需要字符串类型的路由名字符串枚举直接满足要求。// RouterModule.push 的实现publicstaticpush(info:NavRouterInfo,animated?:boolean){RouterModule.stack.pushPathByName(info.url,info.param);// url 为字符串}为什么用注释分组而非嵌套枚举你可能注意到代码中使用注释// --- 练习 ---等来分组。为什么不使用嵌套枚举或命名空间因为 ArkTS 不支持枚举嵌套。注释分组是最务实的替代方案——它虽然没有编译期约束但配合 IDE 的代码折叠功能阅读体验尚可。三、RouterTableBuilder 注册表有了枚举定义的路由名还需要将它们与实际的页面 Builder 关联起来。这就是RouterTable的作用// product/entry/src/main/ets/model/RouterTable.etsimport{RouterMap}fromcommonlib;exportclassRouterTable{staticbuilderMap:Mapstring,WrappedBuilder[object]newMap();publicstaticrouterInit(){// 主页RouterTable.builderMap.set(RouterMap.MAIN_PAGE,wrapBuilder(MainEntryBuilder));RouterTable.builderMap.set(RouterMap.LOGIN_PAGE,wrapBuilder(LoginPageBuilder));// 练习模块来自 homepage featureRouterTable.builderMap.set(RouterMap.CHAPTER_PRACTICE_PAGE,wrapBuilder(ChapterPracticeBuilder));RouterTable.builderMap.set(RouterMap.SEARCH_PAGE,wrapBuilder(SearchPageBuilder));// 课程模块来自 topicpage featureRouterTable.builderMap.set(RouterMap.ANSWER_QUESTIONS_PAGE,wrapBuilder(AnswerQuestionsBuilder));RouterTable.builderMap.set(RouterMap.WORD_CARD_PAGE,wrapBuilder(WordCardPageBuilder));RouterTable.builderMap.set(RouterMap.SPEECH_EVAL_PAGE,wrapBuilder(SpeechEvalPageBuilder));// 我的模块来自 minepage featureRouterTable.builderMap.set(RouterMap.SET_UP_PAGE,wrapBuilder(SetUpPageBuilder));RouterTable.builderMap.set(RouterMap.LEARNING_PLAN_PAGE,wrapBuilder(LearningPlanSettingPageBuilder));RouterTable.builderMap.set(RouterMap.DASHBOARD_PAGE,wrapBuilder(DashboardPageBuilder));// ... 40 路由注册}}设计要点builderMap以RouterMap枚举值为 key以WrappedBuilder为 value所有注册集中在routerInit()中统一调用在应用启动时完成wrapBuilder()将Builder函数包装为可反射调用的形式四、RouterModule统一导航封装有了路由表和枚举还需要一个统一的导航工具类来消费它们// commons/commonLib/src/main/ets/utils/RouterModule.etsclassRouterModule{publicstaticstack:NavPathStacknewNavPathStack();// 页面跳转publicstaticpush(info:NavRouterInfo,animated?:boolean){RouterModule.stack.pushPathByName(info.url,info.param,info.onPop,animated);}// 页面替换publicstaticreplace(info:NavRouterInfo){RouterModule.stack.replacePathByName(info.url,info.param);}// 页面回退publicstaticpopTboolean(result?:T,animated?:boolean){RouterModule.stack.pop(result,animated);}// 页面栈清空publicstaticclear(animated?:boolean){RouterModule.stack.clear(animated);}}业务代码中使用时RouterMap 枚举值 RouterModule 的组合确保了类型安全// MainPage.ets - 页面跳转示例RouterModule.push({url:RouterMap.WORD_CARD_PAGE});// 无参数跳转RouterModule.push({url:RouterMap.LOGIN_PAGE,param:false});// 带参数跳转RouterModule.push({url:RouterMap.ANSWER_QUESTIONS_PAGE,param:1});// 字符串参数RouterModule.push({url:RouterMap.WORD_CARD_PAGE,param:{id:1}});// 对象参数对比直接使用字符串字面量// ❌ 不好的做法硬编码字符串RouterModule.push({url:WordCardPage});// 拼写错误不会报错RouterModule.push({url:word-card-page});// 大小写不一致// ✅ 好的做法使用枚举RouterModule.push({url:RouterMap.WORD_CARD_PAGE});// 拼写错误编译报错五、route_map.json与系统路由的桥接除了代码层面的路由管理HarmonyOS 还要求每个 feature 模块在route_map.json中注册 NavDestination 路由{routerMap:[{name:HomePage,pageSourceFile:src/main/ets/pages/MainPage.ets,buildFunction:HomePageBuilder,data:{description:首页}},{name:FeaturedCourses,pageSourceFile:src/main/ets/pages/FeaturedCourses.ets,buildFunction:FeaturedCoursesBuilder,data:{description:精选课程}}]}这里的name值必须与RouterMap枚举的值一致否则运行时无法匹配。因此 RouterMap 枚举值实际上起着代码层路由名 ↔ 系统层路由配置的桥梁作用。六、字符串枚举 vs 数字枚举 vs 常量对象ArkTS 中定义常量有三种主要方式各有优劣方案示例运行时值序列化IDE 提示反向映射字符串枚举enum E { A a }a✅ 友好✅ 完整❌数字枚举enum E { A }0❌ 无意义✅ 完整✅常量对象const E { A: a }a✅ 友好⚠️ 有限❌对于路由管理字符串枚举是最佳选择因为枚举名即文档——RouterMap.WORD_CARD_PAGE比wordCardPage更具语义重构友好——修改枚举值时所有引用处自动更新不可变——const enum在编译期内联无运行时开销统一管理——所有路由路径在一处定义一目了然七、最佳实践统一枚举命名规范模块_功能_PAGE如LEARNING_PLAN_PAGE、ANSWER_QUESTIONS_PAGE。清晰的命名让开发者看到枚举名就能定位到对应模块。注释分组使用// --- 模块名 ---注释对枚举成员进行分组配合 IDE 代码折叠提升可读性。枚举值尽量简短枚举的值用于路由匹配宜短不宜长。例如WordCardPage而非WordCardManagementPage。与 route_map.json 保持同步新增路由时确保RouterMap枚举、route_map.json、RouterTable.routerInit()三者同步更新。参数类型关联对于带参数的路由建议在跳转处加上注释说明参数类型RouterModule.push({url:RouterMap.ANSWER_QUESTIONS_PAGE,param:1asstring,// ⬅️ param: 章节ID字符串});八、总结RouterMap枚举 RouterTable注册表 RouterModule封装的三层路由架构是我们项目中的核心基础设施RouterMap用字符串枚举集中定义 40 路由路径杜绝硬编码RouterTable将枚举值映射到具体的页面 Builder实现声明式路由注册RouterModule在 NavPathStack 之上提供统一的 push/pop/replace/clear 接口这套模式的核心价值在于一处修改全局生效。当你需要修改某个路由路径时只需更新RouterMap枚举值所有引用处自动适配。对于大型多模块项目而言这种集中管理的设计模式是保证代码质量的关键。