关于鸿蒙配置HMRouter的问题,比如白屏等
参考1:https://gitee.com/hadss/hmrouter/blob/master/HMRouterLibrary/README.md
参考2:https://ohpm.openharmony.cn/#/cn/detail/@hadss%2Fhmrouter
配置流程:
一、下载安装
使用ohpm安装依赖
ohpm install @hadss/hmrouter
注意:这里你安装的版本号,和后面在hvigor/hvigor-config.json中配置的版本号一定要一致,否则盲目的根据官方会出现白屏,因为版本不一致
二、使用配置
编译插件配置
1.修改工程的hvigor/hvigor-config.json
文件,加入路由编译插件
{"dependencies": {"@hadss/hmrouter-plugin": "^1.2.0-rc.0"// 使用npm仓版本号},// ...其他配置
}
2.在使用到HMRouter的模块中引入路由编译插件,修改hvigorfile.ts
示例:
// ./hvigorfile.ts 工程根目录的hvigorfile.ts
import { appTasks } from '@ohos/hvigor-ohos-plugin';export default {system: appTasks,plugins:[]
}// entry/hvigorfile.ts entry模块的hvigorfile.ts
import { hapTasks } from '@ohos/hvigor-ohos-plugin';
import { hapPlugin } from '@hadss/hmrouter-plugin';export default {system: hapTasks,plugins: [hapPlugin()] // 使用HMRouter标签的模块均需要配置,与模块类型保持一致
}// libHar/hvigorfile.ts libHar模块的hvigorfile.ts
import { harTasks } from '@ohos/hvigor-ohos-plugin';
import { harPlugin } from '@hadss/hmrouter-plugin';export default {system: harTasks,plugins:[harPlugin()] // 使用HMRouter标签的模块均需要配置,与模块类型保持一致
}// libHsp/hvigorfile.ts libHsp模块的hvigorfile.ts
import { hspTasks } from '@ohos/hvigor-ohos-plugin';
import { hspPlugin } from '@hadss/hmrouter-plugin';export default {system: hspTasks,plugins: [hspPlugin()] // 使用HMRouter标签的模块均需要配置,与模块类型保持一致
}
如果模块是Har则使用
harPlugin()
, 模块是Hsp则使用hspPlugin()
, 模块是Hap则使用hapPlugin()
3.在项目根目录创建路由编译插件配置文件hmrouter_config.json
(可选)
{// 如果不配置则扫描src/main/ets目录,对代码进行全量扫描,如果配置则数组不能为空,建议配置指定目录可缩短编译耗时"scanDir": ["src/main/ets/components","src/main/ets/interceptors"],// 默认为false,调试排除错误时可以改成true,不删除编译产物"saveGeneratedFile": false,// 默认为false,不自动配置混淆规则,只会生成hmrouter_obfuscation_rules.txt文件帮助开发者配置混淆文件;如果设置为true,会自动配置混淆规则,并删除hmrouter_obfuscation_rules.txt文件"autoObfuscation": false,// 默认模板文件,不配置时使用插件内置模板"defaultPageTemplate": "./templates/defaultTemplate.ejs",// 特殊页面模版文件,匹配原则支持文件通配符"customPageTemplate": [{"srcPath": ["**/component/Home/**/*.ets"],"templatePath": "templates/home_shopping_template.ejs"},{"srcPath": ["**/live/**/*.ets"],"templatePath": "templates/live_template.ejs"}]
}
配置文件读取规则为 模块 > 工程 > 默认
优先使用本模块内的配置,如果没有配置,则使用工程目录中的配置,若找不到则使用默认配置
工程配置
由于拦截器、生命周期和自定义转场动画会在运行时动态创建实例,因此需要进行如下配置,使得HMRouter路由框架可以动态导入项目中的模块
1.在工程目录下的build-profile.json5
中,配置useNormalizedOHMUrl
属性为true
{"app": {"products": [{"name": "default","signingConfig": "default","compatibleSdkVersion": "5.0.0(12)","runtimeOS": "HarmonyOS","buildOption": {"strictMode": {"useNormalizedOHMUrl": true}}}],// ...其他配置}
}
2.在oh-package.json5
中配置对Har和Hsp的依赖,这里需要注意依赖的模块名称需要与module.json5中moduleName、oh-package.json5中name保持一致。
详见官网文档:动态import实现方案介绍 中的备注部分
{"dependencies": {"AppHar": "file:../AppHar",// AppHar库可以正确动态创建拦截器、生命周期和自定义转场动画对象"@app/har": "file:../AppHar"// 错误使用方式,无法动态创建对象}
}
快速开始
在UIAbility或者启动框架AppStartup中初始化路由框架
export default class EntryAbility extends UIAbility {onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {HMRouterMgr.init({context: this.context})}
}
使用启动框架请查看:如何在启动框架中初始化HMRouter
定义路由入口
HMRouter依赖系统Navigation能力,所以必须在页面中定义一个HMNavigation
容器,并设置相关参数,具体代码如下:
@Entry
@Component
export struct Index {modifier: NavModifier = new MyNavModifier();build() {// @Entry中需要再套一层容器组件,Column或者StackColumn(){// 使用HMNavigation容器HMNavigation({navigationId: 'mainNavigation', homePageUrl: 'MainPage',options: {standardAnimator: HMDefaultGlobalAnimator.STANDARD_ANIMATOR,dialogAnimator: HMDefaultGlobalAnimator.DIALOG_ANIMATOR,modifier: this.modifier}})}.height('100%').width('100%')}
}class MyNavModifier extends AttributeUpdater<NavigationAttribute> {initializeModifier(instance: NavigationAttribute): void {instance.hideNavBar(true);}
}
Navigation的系统属性通过modifier
传递,部分modifier
不支持的属性使用options
设置
定义拦截器
使用@HMInterceptor
标签定义拦截器,并实现IHMInterceptor
接口
@HMInterceptor({ interceptorName: 'JumpInfoInterceptor', global: true })
export class JumpInfoInterceptor implements IHMInterceptor {handle(info: HMInterceptorInfo): HMInterceptorAction {let connectionInfo: string = info.type === 'push' ? 'jump to' : 'back to';Logger.info(`${info.srcName} ${connectionInfo} ${info.targetName}`)return HMInterceptorAction.DO_NEXT;}
}
定义生命周期
使用@HMLifecycle
标签定义生命周期处理器,并实现IHMLifecycle
接口
@HMLifecycle({ lifecycleName: 'PageDurationLifecycle' })
export class PageDurationLifecycle implements IHMLifecycle {private time: number = 0;onShown(ctx: HMLifecycleContext): void {this.time = new Date().getTime();}onHidden(ctx: HMLifecycleContext): void {const duration = new Date().getTime() - this.time;Logger.info(`Page ${ctx.navContext?.pathInfo.name} stay ${duration}`);}
}
自定义转场动画
通过@HMAnimator
标签定义转场动画,并实现IHMAnimator
接口
@HMAnimator({ animatorName: 'liveCommentsAnimator' })
export class liveCommentsAnimator implements IHMAnimator {effect(enterHandle: HMAnimatorHandle, exitHandle: HMAnimatorHandle): void {// 入场动画enterHandle.start((translateOption: TranslateOption, scaleOption: ScaleOption,opacityOption: OpacityOption) => {translateOption.y = '100%'})enterHandle.finish((translateOption: TranslateOption, scaleOption: ScaleOption,opacityOption: OpacityOption) => {translateOption.y = '0'})enterHandle.duration = 500// 出场动画exitHandle.start((translateOption: TranslateOption, scaleOption: ScaleOption,opacityOption: OpacityOption) => {translateOption.y = '0'})exitHandle.finish((translateOption: TranslateOption, scaleOption: ScaleOption,opacityOption: OpacityOption) => {translateOption.y = '100%'})exitHandle.duration = 500}
}
自定义转场动画的详细使用:查看详情
路由跳转使用
使用@HMRouter
标签定义页面,绑定拦截器、生命周期及自定义转场动画
@HMRouter({ pageUrl: '/pageB', lifecycle: 'pageLifecycle', animator: 'pageAnimator' })
@Component
export struct PageB {// 获取生命周期中定义的状态变量@State model: ObservedModel | null = (HMRouterMgr.getCurrentLifecycleOwner().getLifecycle() as PageLifecycle).model@State param: HMPageParam = HMRouterMgr.getCurrentParam(HMParamType.all)build() {Column() {Text(`${this.model?.property}`)Text(`${this.param.paramsMap?.get('msg')}`)}}
}
定义页面PageA,使用HMRouterMgr.push
执行路由跳转至PageB
const PAGE_URL: string = '/pageA'@HMRouter({ pageUrl: PAGE_URL })
@Component
export struct PageA {build() {Column() {Button('Push').onClick(() => {HMRouterMgr.push({ pageUrl: 'pageB?msg=abcdef' })})}
}
}
路由跳转支持URL带参数的方式,例如定义的页面pageUrl:
/pages1/users
,跳转时可以指定pageUrl为:/pages1/users?msg=1234
通过HMRouterMgr.getCurrentParam传入HMParamType.all获取URL的参数内容
服务路由使用
服务路由用于类似服务提供发现机制(Service Provider Interface),通过不依赖实现模块的方式获取接口实例并调用方法,当前仅提供方法级的调用
export class CustomService {@HMService({ serviceName: 'testConsole' })testConsole(): void {Logger.info('调用服务 testConsole')}@HMService({ serviceName: 'testFunWithReturn' })testFunWithReturn(param1: string, param2: string): string {return `调用服务 testFunWithReturn:${param1} ${param2}`}@HMService({ serviceName: 'testAsyncFun', singleton: true })async asyncFunction(): Promise<string> {return new Promise((resolve) => {resolve('调用异步服务 testAsyncFun')})}
}@HMRouter({ pageUrl: 'test://MainPage' })
@Component
export struct Index {build() {Row() {Column({ space: 8 }) {Button('service').onClick(() => {HMRouterMgr.request('testConsole')Logger.info(HMRouterMgr.request('testFunWithReturn', 'home', 'service').data)HMRouterMgr.request('testAsyncFun').data.then((res: string) => Logger.info(res))})}.width('100%')}.height('100%')
}
}
当前不支持同时和其他注解混用,也不支持静态方法
// 不支持类与类方法同时添加 @HM* 装饰器
@HMLifecycle({ serviceName: 'lifecycleName' })
export class CustomServiceErr1 {@HMService({ serviceName: 'testConsole' }) // 类已经添加 @HMLifecycle 装饰器,@HMService 无法识别testConsole(): void {Logger.info('调用服务 testConsole')}
}// 不支持在静态方法上添加 @HMService 装饰器
export class CustomServiceErr2 {@HMService({ serviceName: 'testConsole' }) // 静态方法添加 @HMService 装饰器,调用时会报错static testConsole(): void {Logger.info('调用服务 testConsole')}
}
混淆配置说明
@hadss/hmrouter-plugin(1.0.0-rc.6)
版本之后HMRouter支持混淆自动配置白名单
开发者在build-profile.json5
中配置混淆选项enable为true(开启混淆),如下所示,并且在当前模块hmrouter_config.json
中配置autoObfuscation
为true(默认为false)。
HMRouter会自动生成HMRouter必须的白名单配置。将其保存在当前模块hmrouter_obfuscation_rules.txt
文件中,并在编译阶段将该文件自动加入到混淆配置文件files
列表中,实现混淆自动配置效果。
// build-profile.json5
{"buildOptionSet": [{"name": "release","arkOptions": {"obfuscation": {"ruleOptions": {"enable": true,"files": ["./obfuscation-rules.txt"]}}}},],
}
// hmrouter_config.json
{"saveGeneratedFile": true,"autoObfuscation": true
}
如果将
autoObfuscation
改为false,则只会生成混淆规则文件,但不会自动修改模块的混淆配置。开发者需要自行将生成的混淆文件
hmrouter_obfuscation_rules.txt
文件加入到混淆配置文件files
列表中。
HMRouter手动配置混淆请参考HMRouter混淆配置
HMRouter标签的使用规则
标签中的字符串属性支持使用常量,详见FAQ
路由标签@HMRouter
@HMRouter(pageUrl, isRegex, regexPriority, dialog, singleton, interceptors, lifecycle, animator)
标签使用在自定义组件struct
上,且该自定义组件需要添加export
关键字
- pageUrl: string, 用来表示NavDestination,必填
1.支持使用本文件或者本模块定义的常量,或者Class中定义的静态变量
2.pageUrl配置支持的格式说明:
- 支持普通字符串定义,路由跳转采用全路径方式匹配,例如定义demo://xxxx,路由跳转时pageUrl=demo://xxx可以使用全路径匹配
3.pageUrl路由匹配优先级说明:优先全路径匹配,然后匹配正则格式路由,正则内的路由匹配优先级通过regexPriority属性设置;例如定义了两个路由:pageUrl/detail, pageUrl/.* ;当路由跳转时传入pageUrl=/pages/detail,将匹配第一个/pages/detail,当路由跳转时传入pageUrl=/pages/abcdef时,将匹配/pages/.*定义的路由页面
- isRegex:boolean, 标识配置的pageUrl是否是正则表达式,如果配置为正则,会影响页面跳转效率,配置为true时,需要确保pageUrl为正确的正则表达式格式,非必填
- regexPriority: number, pageUrl正则匹配优先级,数字越大越先匹配,默认值为0,优先级相同时,不保证先后顺序,非必填,默认为0
- dialog: boolean, 是否是Dialog类型页面,非必填,默认为false
- singleton: boolean, 是否是单例页面,单例页面即表示在一个HMNavigation容器中,只有一个此页面,非必填,默认为false
- interceptors: string[],
@HMInterceptor
标记的拦截器名称列表,非必填 - lifecycle: string,
@HMLifecycle
标记的生命周期处理实例,非必填 - animator: string,
@HMAnimator
标记的自定义转场实例,非必填
示例:
@HMRouter({pageUrl: 'pageOne',interceptors: ['LoginInterceptor'],lifecycle: 'pageLifecycle',animator: 'pageAnimator'
})
@Component
export struct PageOne {build() {}
}// constants.ets
export class Constants {static readonly PAGE: string = 'pageTwo'
}@HMRouter({ pageUrl: Constants.PAGE })
@Component
export struct PageOne {build() {}
}
拦截器标签 @HMInterceptor
标记在实现了IHMInterceptor
的对象上,声明此对象为一个拦截器
- interceptorName: string, 拦截器名称,必填
- priority: number, 拦截器优先级,数字越大优先级越高,非必填,默认为9;
- global: boolean, 是否为全局拦截器,当配置为true时,所有跳转均过此拦截器;默认为false,当为false时需要配置在@HMRouter的interceptors中才生效。
执行时机:
在路由栈发生变化前,转场动画发生前进行回调。 1.当发生push/replace路由时,pageUrl为空时,拦截器不会执行,需传入pageUrl路径;
2.当跳转pageUrl目标页面不存在时,执行全局以及发起页面拦截器,当拦截器未执行DO_REJECT时,然后执行路由的onLost回调
3.当跳转pageUrl目标页面存在时,执行全局,发起页面和目标页面的拦截器;
拦截器执行顺序:
- 按照优先级顺序执行,不区分自定义或者全局拦截器,优先级相同时先执行@HMRouter中定义的自定义拦截器
- 当优先级一致时,先执行srcPage>targetPage>global
srcPage表示跳转发起页面。
targetPage表示跳转结束时展示的页面。
示例:
@HMInterceptor({priority: 9,interceptorName: 'LoginInterceptor'
})
export class LoginInterceptor implements IHMInterceptor {handle(info: HMInterceptorInfo): HMInterceptorAction {if (isLogin) {// 跳转下一个拦截器处理return HMInterceptorAction.DO_NEXT;} else {HMRouterMgr.push({pageUrl: 'loginPage',param: { targetUrl: info.targetName },skipAllInterceptor: true})// 拦截结束,不再执行下一个拦截器,不再执行相关转场和路由栈操作return HMInterceptorAction.DO_REJECT;}}
}
生命周期标签 @HMLifecycle
@HMLifecycle(lifecycleName, priority, global)
标记在实现了IHMLifecycle的对象上,声明此对象为一个自定义生命周期处理器
- lifecycleName: string, 自定义生命周期处理器名称,必填
- priority: number, 生命周期优先级,数字越大优先级越高,非必填,默认为9;
- global: boolean, 是否为全局生命周期,当配置为true时,所有页面生命周期事件会转发到此对象;默认为false
生命周期触发顺序:
按照优先级顺序触发,不区分自定义或者全局生命周期,优先级相同时先执行@HMRouter中定义的自定义生命周期
示例:
@HMLifecycle({ lifecycleName: 'exampleLifecycle' })
export class ExampleLifecycle implements IHMLifecycle {
}
转场动画标签 @HMAnimator
标记在实现了IHMAnimator的对象上,声明此对象为一个自定义转场动画对象
- animatorName: string, 自定义动画名称,必填。
示例:
@HMAnimator({ animatorName: 'exampleAnimator' })
export class ExampleAnimator implements IHMAnimator {effect(enterHandle: HMAnimatorHandle, exitHandle: HMAnimatorHandle): void {}
}
服务标签 @HMServiceProvider
标记在类上,声明此类为一个服务
- serviceName: string,服务名称,必填。
- singleton: boolean,是否是单例,非必填,默认为false
示例:
@HMServiceProvider({ serviceName: ServiceConstants.CLASS_SERVICE, singleton: true })
export class CustomService implements IService {testConsole(): void {Logger.info('Calling service testConsole');}
}
使用HMRouterMgr.getService()
进行调用
const res = HMRouterMgr.getService<IService>(ServiceConstants.CLASS_SERVICE).testFunWithReturn()
服务标签 @HMService
标记在类的方法上,声明此方法为一个服务
- serviceName: string,服务名称,必填。
- singleton: boolean,是否是单例,非必填,默认为false
示例:
export class ExampleClass {@HMService({ serviceName: 'ExampleService', singleton: true })exampleFun(params: string): void {}
}