资讯动态

开发版跨包能跳,安装包却报目标页不存在:HarmonyOS 7 系统路由表的 HAP/HAR/HSP 打包契约实战

发布时间:2026/10/19 2:09:39 来源:尧图企业网站定制
开发版跨包能跳安装包却报目标页不存在HarmonyOS 7 系统路由表的 HAP/HAR/HSP 打包契约实战本地调试时从菜谱列表进入“营养分析”页一切正常切换成安装包点击同一个入口却只剩空白页日志里出现 route not found。另一个常见现象是HAR 或 HSP 已经构建成功IDE 里也能补全导出的页面函数但宿主 HAP 的最终依赖里根本没有那份产物。代码没有改跳转结果却变了。这类问题通常不是 Navigation API 本身失效而是三份契约没有同时进入安装包router_map.json 声明路由module.json5 把模块绑定到这份路由表oh-package.json5 再把 HAR/HSP 产物纳入宿主依赖。任何一环只存在于开发工作区、没有进入最终构建运行时就找不到目标页。本文交付一套“中式美食”跨包路由的发布前预检逐模块核对 route name、pageSourceFile、buildFunction 和导出 Builder计算 HAP/HSP/HAR 的直接依赖闭包输出可归档的 route-contract-report.json。当前机器只有 API 24 SDK且没有连接 HDC 设备。文中的配置结论来自华为当前官方文档实际执行的 Node.js 断言只验证清单和依赖规则不声称完成 API 26 编译、安装包运行或真机跳转验证。先把故障拆成三层不要一上来改 pushPathByName跨包路由能否成立需要三个问题都回答“是”。契约层关键文件失败信号路由声明resources/base/profile/router_map.jsonroute name 不存在、buildFunction 对不上导出函数模块绑定module.json5routerMap 没指向 $profile:router_map构建后未加载路由表包依赖oh-package.json5 与实际产物HAR/HSP 在工作区存在但宿主 HAP 没有直接依赖官方“Navigation跨包动态路由”文档说明每个 HAP、HSP、HAR 都要独立配置自己的 router_map.json并在 module.json5 中用 routerMap 指向对应 profile。buildFunction 必须和源码中导出的 Builder 函数同名否则会出现编译错误。API 23 起系统路由表还支持跳转到按需加载 HSP 中的页面。因此“开发版能跳”只证明当前工作区某条解析路径成立不能证明最终包包含完整契约。尤其是用源码依赖调试、发布时换成 HAR/HSP 产物的项目两条构建链很容易不一致。最小正确配置路由名、文件和 Builder 必须三点对齐以营养分析页面为例HSP 模块中的 route map 可以这样写{routerMap:[{name:nutrition/detail,pageSourceFile:src/main/ets/pages/NutritionDetail.ets,buildFunction:NutritionDetailBuilder,data:{module:nutritionHsp,scene:recipe}}]}对应 module.json5 不能只声明 pages还要把系统路由表接进模块{ module: { name: nutritionHsp, type: shared, deliveryWithInstall: true, pages: $profile:main_pages, routerMap: $profile:router_map } }页面源码则要导出同名 Builderimport{NavPathStack}fromkit.ArkUI;BuilderexportfunctionNutritionDetailBuilder(){NutritionDetailPage();}Componentstruct NutritionDetailPage{build(){Column({space:12}){Text(营养分析)Text(按每 100 克展示能量与营养素)}.width(100%).padding(20)}}宿主页只使用稳定的 route name不把目标源码路径写进业务代码EntryComponentstruct RecipeHomePage{privatestack:NavPathStacknewNavPathStack();build(){Navigation(this.stack){Button(查看营养分析).onClick((){this.stack.pushPathByName(nutrition/detail,{recipeId:mapo-tofu});})}}}route name 是跨模块契约键。不要让调用方依赖 pageSourceFile也不要把同一个名字复制到多个模块指向不同页面。系统路由表的目标是把路由发现从静态 import 中解开不是取消发布前校验。data 和 customData 不是一回事routerMap 的 data 是字符串键值表单个值不应超过 128 字节customData 可以保存任意可序列化类型但总长度不能超过 4096 字节读取后通常需要 JSON.parse。它们适合携带页面元数据不适合塞入完整菜谱、图片二进制或用户隐私。可以在进入页面前读取路由配置做灰度或能力判断constconfigthis.stack.getConfigInRouteMap(nutrition/detail);if(!config){this.showRouteMissingDialog(nutrition/detail);return;}constmoduleNameconfig.data?.module??unknown;hilog.info(0x0000,RouteCheck,target module: %{public}s,moduleName);this.stack.pushPathByName(nutrition/detail,{recipeId:this.recipeId});这里的检查只能改善错误提示不能替代构建时预检。若最终包里连目标 HSP 都不存在运行时再友好地报错也只是兜底。案例一route map 写对了module.json5 却没有绑定假设 nutritionHsp 目录里已经有 router_map.json 和 NutritionDetail.ets。开发者在 IDE 中打开文件、跳转定义都正常但 module.json5 忘了 routerMap 字段。源码存在不代表系统路由表会被装载。发布前预检先把每个模块抽象为一份清单interfaceRouteRow{name:string;pageSourceFile:string;buildFunction:string;data?:Recordstring,string;customData?:unknown;}interfaceModuleContract{moduleName:string;moduleType:entry|feature|shared|har;routerMapProfile?:string;routes:RouteRow[];exportedBuilders:string[];directDependencies:string[];}第一组规则只检查“模块自己是否自洽”functionvalidateRouteContract(module:ModuleContract):string[]{consterrors:string[][];if(module.routes.length0module.routerMapProfile!$profile:router_map){errors.push(module.moduleName: ROUTER_MAP_PROFILE_MISSING);}constseennewSetstring();for(constrouteofmodule.routes){if(seen.has(route.name)){errors.push(module.moduleName: DUPLICATE_ROUTE_NAME:route.name);}seen.add(route.name);if(!route.pageSourceFile.endsWith(.ets)){errors.push(module.moduleName: INVALID_PAGE_SOURCE:route.name);}if(!module.exportedBuilders.includes(route.buildFunction)){errors.push(module.moduleName: BUILDER_NOT_EXPORTED:route.buildFunction);}for(const[key,value]ofObject.entries(route.data??{})){if(Buffer.byteLength(value,utf8)128){errors.push(module.moduleName: DATA_VALUE_TOO_LARGE:key);}}if(route.customData!undefinedBuffer.byteLength(JSON.stringify(route.customData),utf8)4096){errors.push(module.moduleName: CUSTOM_DATA_TOO_LARGE:route.name);}}returnerrors;}在这个案例中预检输出应包含nutritionHsp: ROUTER_MAP_PROFILE_MISSING修复不是修改 pushPathByName也不是把页面挪回 HAP只需让 module.json5 明确绑定 profile并确认构建目录包含对应资源。随后再检查 buildFunction 是否和导出函数完全一致包含大小写。为什么不建议“扫描到函数就自动补配置”自动补配置会掩盖契约缺失。更稳妥的方式是让预检失败并输出文件级定位由模块维护者显式确认 route name。路由名属于跨模块公共接口自动生成一旦变化会直接影响深链、测试和旧版本恢复。案例二HSP 构建成功但宿主 HAP 没有直接依赖第二类故障更像发布问题。nutritionHsp 可以独立构建产物也已经复制到 common 目录宿主 entry 的 oh-package.json5 却仍指向旧文件或者只依赖了一个“会继续依赖 nutritionHsp”的中间 HSP。华为官方 HSP 文档明确说明应用内 HSP 跟随宿主 APP不能独立发布HSP 可以依赖 HAR/HSP也可以被 HAP/HSP 依赖但不允许循环依赖和传递依赖。也就是说宿主不能假设“我依赖 AA 依赖 B所以我自动获得 B”。宿主声明应明确列出实际需要的包例如{ dependencies: { food/navigation-contract: file:../common/navigation_contract.har, food/nutrition: file:../common/nutrition_hsp.har } }示例路径要以项目实际构建产物为准。预检关注的不是后缀看起来像什么而是依赖文件存在、摘要和当前构建一致、宿主对目标模块有直接依赖。interfacePackageEdge{from:string;to:string;artifactPath:string;artifactExists:boolean;}functionvalidatePackageClosure(hostModule:string,requiredRouteOwners:string[],edges:PackageEdge[]):string[]{consterrors:string[][];constdirectnewMap(edges.filter((edge)edge.fromhostModule).map((edge)[edge.to,edge]));for(constownerofrequiredRouteOwners){constedgedirect.get(owner);if(!edge){errors.push(hostModule: DIRECT_DEPENDENCY_MISSING:owner);continue;}if(!edge.artifactExists){errors.push(hostModule: ARTIFACT_NOT_FOUND:edge.artifactPath);}}returnerrors;}如果 entry 只依赖 menuHsp而 nutritionHsp 只出现在 menuHsp 的 dependencies 中预检仍然报entry: DIRECT_DEPENDENCY_MISSING:nutritionHsp这正是预检的价值它验证最终宿主需要的路由所有者而不是相信工作区里“总能搜到这个模块”。再加一条全局规则路由名不能跨模块撞车单个模块内部没有重复不代表整个应用没有冲突。把所有模块的 routes 合并按 name 建索引functionvalidateGlobalRouteNames(modules:ModuleContract[]):string[]{constownersnewMapstring,string[]();for(constmoduleofmodules){for(constrouteofmodule.routes){constlistowners.get(route.name)??[];list.push(module.moduleName);owners.set(route.name,list);}}return[...owners.entries()].filter(([,list])list.length1).map(([name,list])GLOBAL_ROUTE_CONFLICT:name:list.join(,));}例如 recipe/detail 已经由 entry 声明另一个 HAR 又声明同名页面应该在打包前直接失败。不要依赖运行时加载顺序决定“谁覆盖谁”这种结果很难在不同构建模式之间保持稳定。HSP 资源引用也要纳入检查HSP 页面中的资源推荐通过 $r 引用。官方文档提醒相对路径可能按调用方模块解析跨包后得到的资源位置并不一定和独立调试相同。Image($r(app.media.nutrition_plate)).width(240).height(160).objectFit(ImageFit.Cover)预检可以扫描路由页面中的相对资源字面量把 …/resources 或 ./media 之类的路径列为人工复核项。它不需要擅自改源码只要让发布清单明确“这条路径在宿主上下文是否仍然成立”。生成可归档的 route-contract-report.json控制台日志适合开发时看发布门禁需要稳定文件。建议输出如下结构{schemaVersion:1,hostModule:entry,modulesChecked:[entry,nutritionHsp,navigationHar],routeCount:7,errors:[],warnings:[nutritionHsp: RELATIVE_RESOURCE_REFERENCE_REVIEW_REQUIRED],evidence:{routerMaps:3,moduleBindings:3,directPackageEdges:2}}报告应和构建输入一起归档而不是只保存一张“跳转成功”的截图。这样当发布包出现 route not found 时可以比较两次报告中的 route count、模块绑定和依赖边快速判断是源码变化还是打包输入变化。宿主断言先证明预检器会拦住错误契约本文配套的 Node.js 断言覆盖以下情况有 routes 但 module.json5 未绑定 $profile:router_map必须失败。buildFunction 未出现在导出 Builder 列表必须失败。同一模块内路由名重复必须失败。不同模块声明同名 route必须报告全局冲突。data 单值超过 128 字节必须失败。customData 总长度超过 4096 字节必须失败。宿主没有直接依赖目标 HSP即使中间包依赖它也必须失败。直接依赖存在但产物文件缺失必须失败。配置完整、直接依赖闭合时错误列表为空。这些断言验证的是纯数据预检器不是 ArkTS 编译器、Hvigor 打包器或设备运行时。真正的发布验收仍需在目标 SDK 和目标设备补齐。目标环境复现与验收在目标 HarmonyOS SDK 中分别构建 HAR/HSP记录产物路径和摘要。检查每个模块的 router_map.json、module.json5 和导出 Builder。运行预检器保存 route-contract-report.json错误必须为 0。构建宿主 HAP/APP确认 oh-package.json5 使用的是本次产物而非旧缓存。在目标设备安装后从冷启动、前台恢复和深链入口分别进入目标页面。对按需 HSP 场景分别验证首次下载、下载失败、重试和离线兜底。记录 route name、宿主版本、模块版本、构建摘要和原始日志。回退到上一版产物确认预检报告能准确显示依赖或路由差异。系统路由表支持模拟器但不支持 Previewer。若只在 Previewer 验证页面布局不能据此判断跨包 Navigation 路由已经成立。文件级发布检查表每个 HAP/HSP/HAR 都有独立 router_map.json。每个含 routes 的模块都在 module.json5 绑定 $profile:router_map。每条 name 在全应用唯一调用方只依赖稳定 route name。pageSourceFile 指向实际 ETS 文件。buildFunction 与导出的 Builder 函数完全同名。data 单值不超过官方限制customData 总长度受控。宿主对目标 HAR/HSP 有明确直接依赖不假设传递依赖。依赖路径、产物摘要和本次构建一致没有引用旧缓存。HSP 资源优先使用 $r所有相对资源路径已复核。没有 HSP 循环依赖。系统路由表验收使用模拟器或设备不把 Previewer 当运行证据。route-contract-report.json 已归档并和上一版做差异比较。宿主断言、目标 SDK 编译、安装包验证和真机结果分开记录。证据边界与官方资料本文依据华为当前官方的 Navigation 跨包动态路由、module 配置文件 routerMap 标签和应用内 HSP 开发指南整理。官方资料可以证明系统路由表的配置方式、字段约束、Builder 绑定、HAP/HAR/HSP 依赖边界、资源引用建议以及 Previewer 限制。本文的宿主测试只能证明预检算法按这些规则识别缺项不能证明目标应用已经完成 API 26 编译、签名安装、按需 HSP 下载或真机跳转。Navigation跨包动态路由module配置文件 routerMap 标签应用内HSP开发指导

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑