HarmonyOS应用实战-启示散页-67-路由表别散在功能包:让 entry 统一声明 HSP 页面入口
HarmonyOS 应用实战 67路由表别散在功能包让 entry 统一声明 HSP 页面入口HSP 可以提供页面和服务但不应该自己决定宿主暴露哪些入口。功能包各自写pushPath会让路由名、参数类型和下线策略散落新增或删除页面时编译可能通过点击却没有反应。本文解决四个问题区分 HSP 导出能力和 entry 暴露入口把 RouteName 和参数类型收进公共层让 entry 统一登记 HSP 页面验证删除导出、缺参数和入口下线HSP 页面不是宿主入口表HSP 的职责是提供可复用能力entry 的职责是决定应用对用户暴露什么。入口表散在 HSP 里会倒置依赖方向。故障链HSP 新增页面并硬编码 route - entry 不知道该入口 - 下线功能时漏删跳转 - 点击无响应HSP 提供能力entry 决定入口。这个边界不清楚时功能包会把路由名、参数校验和启停策略都写散。公共层只放稳定路由协议HAR 可以保存 RouteName 和参数类型但不引入页面实现。这样 entry 和 HSP 都依赖协议不互相依赖内部细节。exportenumRouteName{HomeHome,DeckEditDeckEdit,DrawingDrawing,FavoriteListFavoriteList}exportinterfaceDeckEditRouteParam{deckId:string;from:home|selector|recovery;}公共层只放协议是为了让 entry 和 HSP 共享稳定名字和参数类型而不是让 HAR 依赖页面实现。HSP 只导出 Builder不注册宿主策略HSP 可以导出页面 Builder 和服务不决定是否启用、是否需要登录、参数是否允许为空。export{DeckEditPageBuilder}from./src/main/ets/pages/DeckEditPage;export{DrawingPageBuilder}from./src/main/ets/pages/DrawingPage;export{DeckService}from./src/main/ets/services/DeckService;HSP 导出 Builder 和服务即可。它不应该决定这个页面在宿主里是否可见也不应该私自处理宿主级入口策略。entry 的 HostRouteRegistry 集中声明入口宿主登记入口时同时写 owner、启用状态和参数校验。interfaceHostRouteRecordT{name:RouteName;ownerModule:entry|libraryHSP;enabled:boolean;validate:(param:T)boolean;}consthostRoutes:HostRouteRecordDeckEditRouteParam[][{name:RouteName.DeckEdit,ownerModule:libraryHSP,enabled:true,validate:(p)p.deckId.length0}];HostRouteRegistry把 owner、启用状态和参数校验放在同一处后续下线页面或调整入口时不会漏查。pageMap 是唯一分发点不管页面来自 entry 还是 HSP都由 entry 的 Navigation 分发。这样入口审计、灰度和下线都在一个地方完成。BuilderfunctionpageMap(name:string,param:object){if(nameRouteName.DeckEdit){DeckEditPageBuilder(paramasDeckEditRouteParam);}elseif(nameRouteName.Drawing){DrawingPageBuilder(paramasDrawingParams);}}pageMap是唯一分发点后跨包页面也能被统一审计。路由失败时可以先查宿主表而不是在多个功能包里搜索字符串。路由验证要故意拆掉一个入口路由检查不能只点正常入口。要临时禁用某个 HostRouteRecord、传空 deckId、移除一个 HSP 导出看宿主是否能给出可解释错误。验证禁用 DeckEdit 后按钮不可进入缺 deckId 被 entry 拦截移除 HSP Builder 时构建或启动检查能发现。故意拆掉一个入口的验证很有必要。正常点击能通只能证明 happy path禁用、缺参、移除导出才能证明边界可靠。HSP 路由排查表点了没反应先查宿主入口表。现象先看哪里处理点击无响应hostRoutes enabled宿主开启入口页面参数为空validate 是否覆盖entry 拦截参数HSP 反向依赖 entryHSP 是否硬编码 route改为导出 Builder点击无响应通常不是按钮问题而是宿主入口表、参数校验或 HSP 导出关系断了。按这个顺序排查会更快。路由协议和页面实现分开审跨 HSP 路由要分两次审查。第一次看协议RouteName 是否稳定、参数类型是否在公共层、是否没有页面依赖。第二次看实现HSP 是否只导出 Builderentry 是否统一注册入口。协议审查HAR - RouteName / Param 类型 实现审查HSP - Builder / Service 宿主审查entry - HostRouteRegistry / pageMap / validate这个顺序能防止把所有问题都归到“路由跳转失败”也能快速定位是协议错、导出错还是宿主表漏了。验证要包括下线场景很多路由只在新增时被测几乎不测下线。真实项目里更危险的是功能下线、HSP 改名、参数升级后旧入口还在。第 67 篇应该把这些场景写进验证。场景构造方式通过标准入口禁用enabledfalse按钮不可进入或给出解释参数升级缺少新增字段validate 拦截不进入页面HSP 导出移除移除 Builder 导出构建或启动检查能发现路由名重名注册重复 RouteNameHostRouteRegistry 拒绝这比只点一次“编辑题库”更能证明路由表集中管理的价值。交付记录写清三层 owner最终说明不要只写“路由统一到 entry”。要写清 HAR、HSP、entry 三层各自承担什么以及哪一层没有被当前会话验证。层owner证据协议HARRouteName 和参数类型能力HSPBuilder 导出清单宿主entryHostRouteRegistry、pageMap、入口截图如果没有跑构建就不要说导出关系已被编译验证如果没有点页面就不要说路由交互已通过。路由表还要服务权限和灰度entry 集中声明 HSP 页面入口后不只是避免字符串分散还能统一处理权限、灰度和下线策略。比如某个导入页面只允许测试版本打开或者某个诊断页只能从设置页进入都应该写在宿主入口表里。interfaceHostRoutePolicy{enabled:boolean;allowedFrom:Arrayhome|settings|selector|diagnostics;buildType:debug|release|all;}这段策略让路由表从“页面名字集合”升级为“宿主入口协议”。文章写到这一步读者才能理解为什么路由表不该散在功能包里功能包不知道宿主发布策略也不应该决定 release 包里哪些入口可见。人工评审时搜硬编码路由名路由集中管理后最直接的复查是搜索硬编码 route 字符串。页面里仍然到处写DeckEdit、Drawing说明协议没有真正收敛。rg-nDeckEdit|Drawing|FavoriteList|pushPath|pushDestinationentry hsp har命中结果不是全部错误但应该分成三类RouteName 定义处、HostRouteRegistry 注册处、页面调用封装处。除此之外的散落字符串都要解释原因。这样读者能把文章里的架构边界变成可执行审查而不是停留在“entry 统一声明”这句口号。跨 HSP 项目尤其需要这一步因为功能包越多路由字符串越容易在示例代码、测试入口和临时按钮里残留。集中表不只是写一份新文件还要逐步清掉旧入口。最后一项看页面下线是否可控路由表集中以后还要验证“下线”这件事是否可控。把某个页面从HostRouteRegistry标记为不可用后首页按钮、设置入口、历史回跳和深层跳转都应该得到同一套处理要么入口不可见要么被宿主拦截并给出解释。若某个功能包还能绕过宿主直接打开页面说明入口权仍然散在包内。这个场景比新增页面更能证明架构边界因为发布后的风险往往来自旧入口残留而不是新入口打不开。如果这一步没有证据文章最多说明“入口表设计合理”不能说明 HSP 页面下线链路已经稳定。真正可交付的路由治理必须同时证明新增、跳转、缺参和下线都回到 entry 的同一张表。小结跨 HSP 路由要让 entry 掌握入口表。HAR 定义协议HSP 导出能力entry 决定暴露和校验这样拆包、下线、灰度和参数错误都能在宿主边界处理。