鸿蒙开发:HMRouter封装教程(二)
在第一篇中,我们已经完成了HMRouter 类型封装和HMRouterUtil 工具类的搭建,让路由调用在代码层面变得更简洁。HMRouter 编译插件配置:让@HMRouter注解真正生效HMRouter 初始化与根容器配置:解决“白屏”“路由不生效”等常见坑测试页面编写:用一组示例页面,把常用 API 全部跑一遍本文完全基于实际工程MyHMRouter,你可以对照代码一步一步完成。强制使用 H
📖 前言
在第一篇中,我们已经完成了 HMRouter 类型封装 和 HMRouterUtil 工具类 的搭建,让路由调用在代码层面变得更简洁。本篇我们继续往下走,聚焦于三个实战主题:
- HMRouter 编译插件配置:让
@HMRouter注解真正生效 - HMRouter 初始化与根容器配置:解决“白屏”“路由不生效”等常见坑
- 测试页面编写:用一组示例页面,把常用 API 全部跑一遍
本文完全基于实际工程 MyHMRouter,你可以对照代码一步一步完成。
一、HMRouter 编译插件配置
HMRouter 的 @HMRouter 注解并不是“自动生效”的,它依赖 编译期插件 去扫描注解、生成路由表和构建器。如果不配置插件,即使你在页面上加了 @HMRouter,运行时也会出现:
- 页面打不开
- 直接白屏
- 控制台无明显报错
1.1 在 hvigor-config.json5 中声明插件依赖
文件: hvigor/hvigor-config.json5
在 dependencies 中加入 HMRouter 插件:
{
"modelVersion": "6.0.2",
"dependencies": {
"@hadss/hmrouter-plugin": "latest"
},
"execution": {
// 这里可以配置编译相关选项
}
}
要点:
- 版本直接使用
"latest",方便后续跟随官方更新 - 这是 项目级 配置,所有模块都会看到这个插件依赖
1.2 在根 hvigorfile.ts 中启用 appPlugin
文件: hvigorfile.ts
import { appTasks } from '@ohos/hvigor-ohos-plugin';
import { appPlugin } from '@hadss/hmrouter-plugin';
export default {
system: appTasks, /* Hvigor 内置插件 */
plugins: [appPlugin({ ignoreModuleNames: [] })] /* 启用 HMRouter app 级插件 */
}
几点说明:
- 必须传入参数对象,否则会出现:
-
No options provided之类的错误
ignoreModuleNames: []表示:
-
- 不忽略任何模块,所有模块都参与 HMRouter 插件处理
1.3 在 entry 模块 hvigorfile.ts 中启用 hapPlugin
文件: entry/hvigorfile.ts
import { hapTasks } from '@ohos/hvigor-ohos-plugin';
import { hapPlugin } from '@hadss/hmrouter-plugin';
export default {
system: hapTasks, /* Hvigor 内置插件 */
plugins: [hapPlugin()] /* 启用 HMRouter hap 级插件 */
}
作用:
- 让
entry这个 HAP 模块参与 HMRouter 的编译过程 - 插件会在编译时扫描
@HMRouter,生成路由表和页面构建代码
1.4 常见问题排查
- 忘记配置插件依赖:
-
- 现象:
@HMRouter加上了,但页面死活跳不过去 - 解决:检查
hvigor/hvigor-config.json5中是否有"@hadss/hmrouter-plugin"
- 现象:
- appPlugin 未传 options:
-
- 现象:构建时报
APP_HMROUTER_PLUGIN相关错误 - 解决:务必写成
appPlugin({ ignoreModuleNames: [] })
- 现象:构建时报
- 只配置了根 hvigorfile.ts,忘了 entry/hvigorfile.ts:
-
- 现象:项目能编译,但路由白屏
- 解决:确认
entry/hvigorfile.ts中已添加hapPlugin()
二、HMRouter 初始化与根容器配置
插件配置好之后,还需要在运行时做两件事:
- 初始化 HMRouter(通常在
EntryAbility中) - 使用 HMNavigation 作为根路由容器(替代直接渲染页面)
2.1 在 EntryAbility 中初始化 HMRouter
文件: entry/src/main/ets/entryability/EntryAbility.ets
核心代码:
import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
import { HMRouterUtil } from '../hmRouter/util/HMRouterUtil';
const DOMAIN = 0x0000;
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
// 这里是系统模板代码,可保留
// 初始化 HMRouter(在 onCreate 中初始化)
HMRouterUtil.openLog('DEBUG');
HMRouterUtil.init({
context: this.context,
logLevel: 'DEBUG'
}).then(() => {
hilog.info(DOMAIN, 'testTag', 'HMRouter init success');
}).catch((err: Error) => {
hilog.error(DOMAIN, 'testTag', 'HMRouter init failed: %{public}s', JSON.stringify(err));
});
}
onWindowStageCreate(windowStage: window.WindowStage): void {
// 设置首个 UI 页面
windowStage.loadContent('pages/Index', (err) => {
if (err.code) {
hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
return;
}
hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.');
});
}
}
关键点:
- 初始化放在
onCreate中,确保 Ability 启动时就完成 HMRouter 初始化 - 使用
HMRouterUtil.init而不是直接调用HMRouterMgr,保持封装一致性 openLog('DEBUG')在调试阶段非常有用,可以看到路由日志
2.2 配置 main_pages.json
文件: entry/src/main/resources/base/profile/main_pages.json
{
"src": [
"pages/Index"
]
}
说明:
src至少要有一个页面,否则会出现 Schema 校验错误- 这里我们指定
pages/Index作为入口页面,对应下面的Index.ets
2.3 使用 HMNavigation 作为根容器
文件: entry/src/main/ets/pages/Index.ets
import { HMNavigation } from '@hadss/hmrouter';
import { AttributeUpdater } from '@kit.ArkUI';
/**
* Navigation 属性修改器:用于隐藏导航栏
*/
class MyNavModifier extends AttributeUpdater<NavigationAttribute> {
initializeModifier(instance: NavigationAttribute): void {
instance.hideNavBar(true);
}
}
@Entry
@Component
export struct Index {
modifier: MyNavModifier = new MyNavModifier();
build() {
Column() {
HMNavigation({
navigationId: 'MainNavigation',
homePageUrl: 'pages/HomePage',
options: {
modifier: this.modifier
}
})
}
.width('100%')
.height('100%')
}
}
设计说明:
Index是整个应用的根组件(@Entry)- 内部使用
HMNavigation承载所有路由页面 homePageUrl设置为'pages/HomePage',即我们的测试主页面navigationId建议统一命名,例如'MainNavigation',方便在代码中引用
2.4 白屏问题排查 checklist
如果你遇到 已启动就白屏,可以按下面顺序排查:
- main_pages.json 是否配置了 pages/Index?
- EntryAbility.onWindowStageCreate 是否 loadContent('pages/Index')?
- Index.ets 是否使用了 @Entry 和 HMNavigation?
- 是否已经配置 hmrouter 插件(hvigor-config.json5 / hvigorfile.ts / entry/hvigorfile.ts)?
- 是否在 EntryAbility.onCreate 中调用了 HMRouterUtil.init?
基本上这几项都正确后,白屏问题就能排除 90%。
三、构建一套完整的测试页面
为了验证封装是否可靠,我们在项目中创建了一套 覆盖常用 API 的测试页,并通过 HomePage 统一入口来访问。
3.1 HomePage:测试入口页
文件: entry/src/main/ets/pages/HomePage.ets
功能:
- 提供七个按钮,分别跳转到:
-
- 详情页
- 参数测试页
- Replace 测试页
- 回调测试页
- 链式调用测试页
- 路由栈测试页
- Options 参数测试页
关键代码节选:
@HMRouter({ pageUrl: 'pages/HomePage' })
@Component
export struct HomePage {
@State message: string = 'HMRouter 测试主页';
// 在 aboutToAppear 中初始化各种测试参数
aboutToAppear() {
this.pushParam = { id: 1, name: '跳转测试' };
// ... 其他参数初始化
}
build() {
Scroll() {
Column() {
Button('1. 跳转到详情页')
.onClick(() => {
if (this.pushParam) {
HMRouterUtil.push('pages/DetailPage', this.pushParam as Object);
}
})
// ... 其他按钮
}
}
}
}
3.2 详情页:测试基本跳转与返回
文件: DetailPage.ets
- 使用
getCurrentParam(HMRouterUtilParamType.all)获取传入参数 - 使用
setPopParam+pop返回上一页并携带结果
@HMRouter({ pageUrl: 'pages/DetailPage' })
@Component
export struct DetailPage {
@State paramInfo: string = '';
aboutToAppear() {
const param = HMRouterUtil.getCurrentParam(HMRouterUtilParamType.all);
this.paramInfo = param ? JSON.stringify(param) : '无参数';
}
build() {
Button('返回上一页')
.onClick(() => {
HMRouterUtil.setPopParam({ result: '从详情页返回', success: true } as Object);
HMRouterUtil.pop();
})
}
}
3.3 参数测试页:验证 HMParamType
文件: ParamPage.ets
- 分别获取:
-
- 所有参数:
HMRouterUtilParamType.all - URL 参数:
HMRouterUtilParamType.urlParam - 路由参数:
HMRouterUtilParamType.routeParam
- 所有参数:
注意:
- 枚举值必须使用
urlParam/routeParam,而不是url/route,否则会有编译错误。
3.4 Replace 测试页:replace / replaceAsync
文件: ReplaceTestPage.ets、ReplaceResultPage.ets
ReplaceTestPage
-
- 同时测试:
replace和replaceAsync - 说明 Replace 会“跳过当前页返回”,验证路由栈行为
- 同时测试:
ReplaceResultPage
-
- 一个简单结果页,点击按钮
pop()返回
- 一个简单结果页,点击按钮
3.5 回调测试页:onArrival / onResult / onLost
文件: CallbackTestPage.ets、CallbackResultPage.ets
- 在
CallbackTestPage中:
-
- 构造
HMRouterUtilPathCallback对象,记录回调日志 - 使用
HMRouterUtil.push('pages/CallbackResultPage', param, undefined, callback)进行跳转
- 构造
- 在
CallbackResultPage中:
-
- 使用
setPopParam设置返回参数,pop()触发上一个页面的onResult
- 使用
3.6 链式调用测试页:to().withParam().push()
文件: ChainTestPage.ets、ChainResultPage.ets
示例:
HMRouterUtil.to('pages/ChainResultPage')
.withParam(this.chainParam1 as Object)
.withNavigation('MainNavigation')
.push();
await HMRouterUtil.to('pages/ChainResultPage')
.withParam(this.chainParam2 as Object)
.pushAsync();
3.7 路由栈测试页:getPathStack / existUrl
文件: StackTestPage.ets、StackTestPage2.ets
- 使用
getPathStack('MainNavigation')查看当前导航栈深度和页面列表 - 使用
existUrl('pages/HomePage')/existUrl('pages/NotExistPage')检查页面是否存在
3.8 Options 测试页:HMRouterUtilOptions
文件: OptionsTestPage.ets、OptionsResultPage.ets
- 在
OptionsTestPage中构造HMRouterUtilOptions:
-
navigationId: 'MainNavigation'skipAllInterceptor: false
- 调用:
HMRouterUtil.push('pages/OptionsResultPage', this.testParam as Object, this.options);
四、实战总结与建议
4.1 工程层面的最佳实践
- 强制使用 HMRouterUtil:
-
- 在业务代码中禁止直接调用
HMRouterMgr,统一通过HMRouterUtil - 方便后续升级、统一处理日志和错误
- 在业务代码中禁止直接调用
- 统一 navigationId:
-
- 建议项目只维护少量(甚至一个)
navigationId,统一为常量
- 建议项目只维护少量(甚至一个)
- 所有路由页面都使用 @HMRouter:
-
- 方便 HMRouter 生成路由表
- 避免字符串硬编码过多
4.2 调试与排错建议
- 优先看 HMRouter 日志:
-
- 使用
HMRouterUtil.openLog('DEBUG') - 观察跳转、拦截、返回等关键节点
- 使用
- 遇到 ArkTS 报错时的思路:
-
arkts-no-untyped-obj-literals:为对象定义interface,在aboutToAppear()中初始化arkts-no-obj-literals-as-types:不要用对象字面量直接当类型arkts-no-classes-as-obj:类不要当对象导出,使用export { Class as Alias }
更多推荐



所有评论(0)