HarmonyOS NEXT鸿蒙(开发进阶)基于HMRouter的路由跳转方案 ...

打印 上一主题 下一主题

主题 1034|帖子 1034|积分 3106

  鸿蒙NEXT开发实战往期必看文章:
一分钟了解”纯血版!鸿蒙HarmonyOS Next应用开发!
“非常详细的” 鸿蒙HarmonyOS Next应用开发学习路线!(从零基础入门到精通)
HarmonyOS NEXT应用开发案例实践总结合(连续更新......)
HarmonyOS NEXT应用开发性能优化实践总结(连续更新......)

HMRouter简介

HMRouter作为HarmonyOS的页面跳转场景解决方案,聚焦解决应用内原生页面的跳转逻辑。
HMRouter底层对系统Navigation进行封装,集成了Navigation、NavDestination、NavPathStack的系统本领,提供了可复用的路由拦截、页面生命周期、自定义转场动画,并且在跳转传参、额外的生命周期、服务型路由方面临系统本领进行了扩展。
目标是让开发者在开发过程中无需关注Navigation、NavDestination容器组件的相关细节及模板代码,屏蔽跳转时的判断逻辑,低落拦截器、自定义转场动画实现复杂度,更好的进行模块间解耦。
特性



  • 基于注解声明路由信息
  • 注解中页面路径支持使用字符串常量定义
  • 支持Har、Hsp、Hap
  • 支持Navigation路由栈嵌套
  • 支持服务型路由
  • 支持路由拦截器(包罗全局拦截、单页面拦截、跳转时一次性拦截)
  • 支持生命周期回调(包罗全局生命周期、单页面生命周期、跳转时一次性生命周期、NavBar生命周期)
  • 内置转场动画(页面、Dialog),可设置方向、透明度、缩放,支持交互式转场动画,同时支持设置某个页面的转场动画、跳转时的一次性动画
  • 支持Dialog范例页面、支持单例页面
依靠版本

HarmonyOS NEXT Developer Beta5及以上
   手机版本:NEXT.0.0.60及以上
  下载安装

使用ohpm安装依靠
  1. ohpm install @hadss/hmrouter
复制代码
或者按需在模块中设置运行时依靠,修改 oh-package.json5 
  1. {
  2.   "dependencies": {
  3.     "@hadss/hmrouter": "^1.0.0-rc.6"
  4.   }
  5. }
复制代码
使用设置

编译插件设置

1.修改项目标hvigor/hvigor-config.json文件,加入路由编译插件
  1. {
  2.   "dependencies": {
  3.     "@hadss/hmrouter-plugin": "^1.0.0-rc.6"
  4.     // 使用npm仓版本号
  5.   },
  6.   // ...其他配置
  7. }
复制代码
2.在模块中引入路由编译插件,修改hvigorfile.ts
  1. import { hapTasks } from '@ohos/hvigor-ohos-plugin';
  2. import { hapPlugin } from '@hadss/hmrouter-plugin';
  3. export default {
  4.   system: hapTasks,
  5.   plugins: [hapPlugin()] // 使用HMRouter标签的模块均需要配置,与模块类型保持一致
  6. }
复制代码
  如果模块是Har则使用harPlugin(), 模块是Hsp则使用hspPlugin()
  3.在项目根目次创建路由编译插件设置文件hmrouter_config.json(可选)
  1. {
  2.   // 如果不配置则扫描src/main/ets目录,对代码进行全量扫描,如果配置则数组不能为空,建议配置指定目录可缩短编译耗时
  3.   "scanDir": [
  4.     "src/main/ets/components",
  5.     "src/main/ets/interceptors"
  6.   ],
  7.   "saveGeneratedFile": false,
  8.   // 默认为false,调试排除错误时可以改成true,不删除编译产物
  9.   "autoObfuscation": false // 默认为false,不自动配置混淆规则,只会生成hmrouter_obfuscation_rules.txt文件帮助开发者配置混淆文件;如果设置为true,会自动配置混淆规则,并删除hmrouter_obfuscation_rules.txt文件
  10. }
复制代码
  设置文件读取规则为 模块 > 工程 > 默认 优先使用本模块内的设置,如果没有设置,则找模块目次的上级目次(最多找三层目次,找到则制止),若找不到则使用默认设置
  工程设置

由于拦截器、生命周期和自定义转场动画会在运行时动态创建实例,因此须要进行如下设置,使得HMRouter路由框架可以动态导入项目中的模块
1.在工程目次下的build-profile.json5中,设置useNormalizedOHMUrl属性为true
  1. {
  2.   "app": {
  3.     "products": [
  4.       {
  5.         "name": "default",
  6.         "signingConfig": "default",
  7.         "compatibleSdkVersion": "5.0.0(12)",
  8.         "runtimeOS": "HarmonyOS",
  9.         "buildOption": {
  10.           "strictMode": {
  11.             "useNormalizedOHMUrl": true
  12.           }
  13.         }
  14.       }
  15.     ],
  16.     // ...其他配置
  17.   }
  18. }
复制代码
2.在oh-package.json5中设置对Har和Hsp的依靠,这里须要注意依靠的模块名称须要与模块的moduleName保持一致
详见官网文档:动态import实现方案先容 中的备注部分
  1. {
  2.   "dependencies": {
  3.     "AppHar": "file:../AppHar",
  4.     // AppHar库可以正确动态创建拦截器、生命周期和自定义转场动画对象
  5.     "@app/har": "file:../AppHar"
  6.     // 错误使用方式,无法动态创建对象
  7.   }
  8. }
复制代码
快速开始

在UIAbility或者启动框架AppStartup中初始化路由框架

  1. export default class EntryAbility extends UIAbility {
  2.   onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
  3.     HMRouterMgr.init({
  4.       context: this.context
  5.     })
  6.   }
  7. }
复制代码
使用启动框架请查看:如何在启动框架中初始化HMRouter
定义路由入口:

  1. @Entry
  2. @Component
  3. export struct Index {
  4.   modifier: NavModifier = new NavModifier();
  5.   build() {
  6.     // @Entry中需要再套一层容器组件,Column或者Stack
  7.     Column(){
  8.       // 使用HMNavigation容器
  9.       HMNavigation({
  10.         navigationId: 'mainNavigation', options: {
  11.           standardAnimator: HMDefaultGlobalAnimator.STANDARD_ANIMATOR,
  12.           dialogAnimator: HMDefaultGlobalAnimator.DIALOG_ANIMATOR,
  13.           modifier: this.modifier
  14.         }
  15.       }) {
  16.         Row() {
  17.           HomeBar()
  18.           HomeView()
  19.         }
  20.         .width('100%')
  21.         .height('100%')
  22.       }
  23.     }
  24.     .height('100%')
  25.     .width('100%')
  26.   }
  27. }
  28. class NavModifier extends AttributeUpdater<NavigationAttribute> {
  29.   initializeModifier(instance: NavigationAttribute): void {
  30.     instance.mode(NavigationMode.Stack);
  31.     instance.navBarWidth('100%');
  32.     instance.hideTitleBar(true);
  33.     instance.hideToolBar(true);
  34.   }
  35. }
复制代码
定义拦截器

  1. @HMInterceptor({ interceptorName: 'JumpInfoInterceptor', global: true })
  2. export class JumpInfoInterceptor implements IHMInterceptor {
  3.   handle(info: HMInterceptorInfo): HMInterceptorAction {
  4.     let connectionInfo: string = info.type === 'push' ? 'jump to' : 'back to';
  5.     console.log(`${info.srcName} ${connectionInfo} ${info.targetName}`)
  6.     return HMInterceptorAction.DO_NEXT;
  7.   }
  8. }
复制代码
定义生命周期

  1. @HMLifecycle({ lifecycleName: 'PageDurationLifecycle' })
  2. export class PageDurationLifecycle implements IHMLifecycle {
  3.   private time: number = 0;
  4.   onShown(ctx: HMLifecycleContext): void {
  5.     this.time = new Date().getTime();
  6.   }
  7.   onHidden(ctx: HMLifecycleContext): void {
  8.     const duration = new Date().getTime() - this.time;
  9.     console.log(`Page ${ctx.navContext?.pathInfo.name} stay ${duration}`);
  10.   }
  11. }
复制代码
自定义转场动画

  1. @HMAnimator({ animatorName: 'liveCommentsAnimator' })
  2. export class liveCommentsAnimator implements IHMAnimator {
  3.   effect(enterHandle: HMAnimatorHandle, exitHandle: HMAnimatorHandle): void {
  4.     // 入场动画
  5.     enterHandle.start((translateOption: TranslateOption, scaleOption: ScaleOption,
  6.       opacityOption: OpacityOption) => {
  7.       translateOption.y = '100%'
  8.     })
  9.     enterHandle.finish((translateOption: TranslateOption, scaleOption: ScaleOption,
  10.       opacityOption: OpacityOption) => {
  11.       translateOption.y = '0'
  12.     })
  13.     enterHandle.duration = 500
  14.     // 出场动画
  15.     exitHandle.start((translateOption: TranslateOption, scaleOption: ScaleOption,
  16.       opacityOption: OpacityOption) => {
  17.       translateOption.y = '0'
  18.     })
  19.     exitHandle.finish((translateOption: TranslateOption, scaleOption: ScaleOption,
  20.       opacityOption: OpacityOption) => {
  21.       translateOption.y = '100%'
  22.     })
  23.     exitHandle.duration = 500
  24.   }
  25. }
复制代码
路由跳转使用

定义页面PageB,绑定生命周期及自定义转场动画
  1. @HMRouter({ pageUrl: 'pageB', lifecycle: 'pageLifecycle', animator: 'pageAnimator' })
  2. @Component
  3. export struct PageB {
  4.   // 获取生命周期中定义的状态变量
  5.   @State model: ObservedModel | null = (HMRouterMgr.getCurrentLifecycleOwner().getLifecycle() as PageLifecycle).model
  6.   @State param: Object = HMRouterMgr.getCurrentParam()
  7.   build() {
  8.     Text(`${this.model?.property}`)
  9.     Text(`${this.param?.msg}`)
  10.   }
  11. }
复制代码
定义页面PageA,并实行路由跳转至PageB
  1. const PAGE_URL: string = 'pageA'
  2. @HMRouter({ pageUrl: PAGE_URL })
  3. @Component
  4. export struct PageA {
  5. build() {
  6.   Column() {
  7.     Button('Push')
  8.       .onClick(() => {
  9.         HMRouterMgr.push({ pageUrl: 'pageB' })
  10.         HMRouterMgr.request({ fromPage: 'PageA', toPage: 'PageB' })
  11.       })
  12.   }
  13. }
  14. }
复制代码
服务路由使用

  1. export class CustomService {
  2.   @HMService({ serviceName: 'testConsole' })
  3.   testConsole(): void {
  4.     console.log('调用服务 testConsole')
  5.   }
  6.   @HMService({ serviceName: 'testFunWithReturn' })
  7.   testFunWithReturn(param1: string, param2: string): string {
  8.     return `调用服务 testFunWithReturn:${param1} ${param2}`
  9.   }
  10.   @HMService({ serviceName: 'testAsyncFun', singleton: true })
  11.   async asyncFunction(): Promise<string> {
  12.     return new Promise((resolve) => {
  13.       resolve('调用异步服务 testAsyncFun')
  14.     })
  15.   }
  16. }
  17. @HMRouter({ pageUrl: 'test://MainPage' })
  18. @Component
  19. export struct Index {
  20. build() {
  21.   Row() {
  22.     Column({ space: 8 }) {
  23.       Button('service').onClick(() => {
  24.         HMRouterMgr.request('testConsole')
  25.         console.log(HMRouterMgr.request('testFunWithReturn', 'home', 'service').data)
  26.         HMRouterMgr.request('testAsyncFun').data.then((res: string) => console.log(res))
  27.       })
  28.     }
  29.     .width('100%')
  30.   }
  31.   .height('100%')
  32. }
  33. }
复制代码
  当前不支持同时和其他注解混用,也不支持静态方法
  1. // 不支持类与类方法同时添加 @HM* 装饰器
  2. @HMLifecycle({ serviceName: 'lifecycleName' })
  3. export class CustomServiceErr1 {
  4.   @HMService({ serviceName: 'testConsole' }) // 类已经添加 @HMLifecycle 装饰器,@HMService 无法识别
  5.   testConsole(): void {
  6.     console.log('调用服务 testConsole')
  7.   }
  8. }
  9. // 不支持在静态方法上添加 @HMService 装饰器
  10. export class CustomServiceErr2 {
  11.   @HMService({ serviceName: 'testConsole' }) // 静态方法添加 @HMService 装饰器,调用时会报错
  12.   static testConsole(): void {
  13.     console.log('调用服务 testConsole')
  14.   }
  15. }
复制代码
肴杂设置阐明

@hadss/hmrouter(1.0.0-rc.6)版本之后HMRouter支持肴杂自动设置白名单
开发者在build-profile.json5中设置肴杂选项enable为true(开启肴杂),如下所示,并且在当前模块hmrouter_config.json中设置autoObfuscation为true(默认为false)。HMRouter会自动生成HMRouter必须的白名单设置。将其生存在当前模块hmrouter_obfuscation_rules.txt文件中,并在编译阶段将该文件自动加入到肴杂设置文件files列表中,实现肴杂自动设置效果。
  1. "buildOptionSet": [
  2.     {
  3.       "name": "release",
  4.       "arkOptions": {
  5.         "obfuscation": {
  6.           "ruleOptions": {
  7.             "enable": true,
  8.             "files": [
  9.               "./obfuscation-rules.txt"
  10.             ]
  11.           }
  12.         }
  13.       }
  14.     },
  15.   ],
复制代码
  1. {
  2.   "saveGeneratedFile": true,
  3.   "autoObfuscation":true
  4. }
复制代码
  如果将autoObfuscation改为false,则只会生成肴杂规则文件,但不会自动修改模块的肴杂设置。 开发者须要自行将生成的肴杂文件hmrouter_obfuscation_rules.txt文件加入到肴杂设置文件files列表中。
  HMRouter手动设置肴杂请参考HMRouter肴杂设置
HMRouter标签的使用规则

路由标签@HMRouter

@HMRouter(pageUrl, dialog, singleton, interceptors, lifecycle, animator) 标签使用在自定义组件struct上,且该自定义组件须要添加export关键字


  • pageUrl: string, 用来表现NavDestination,必填
           支持使用本文件或者本模块定义的常量,或者Class中定义的静态变量
  • dialog: boolean, 是否是Dialog范例页面,非必填,默认为false
  • singleton: boolean, 是否是单例页面,单例页面即表现在一个HMNavigation容器中,只有一个此页面,非必填,默认为false
  • interceptors: string[], @HMInterceptor标记的拦截器名称列表,非必填
  • lifecycle: string, @HMLifecycle标记的生命周期处置处罚实例,非必填
  • animator: string, @HMAnimator标记的自定义转场实例,非必填
示例:
  1. // pageUrl配置支持常量,或者class的静态属性,仅在编译期起作用
  2. @HMRouter({ pageUrl: 'pageOne', interceptors: ['LoginInterceptor'], lifecycle: 'pageLifecycle', animator: 'pageAnimator' })
  3. @Component
  4. export struct PageOne {
  5.   
  6.   build() {
  7.   }
  8. }
  9. // constants.ets
  10. export class Constants{
  11.   static readonly PAGE: string = 'pageTwo'
  12. }
  13. @HMRouter({ pageUrl: Constants.PAGE})
  14. @Component
  15. export struct PageOne {
  16.   build() {
  17.   }
  18. }
复制代码
拦截器标签 @HMInterceptor

标记在实现了IHMInterceptor的对象上,声明此对象为一个拦截器


  • interceptorName: string, 拦截器名称,必填
  • priority: number, 拦截器优先级,数字越大优先级越高,非必填,默认为9;
  • global: boolean, 是否为全局拦截器,当设置为true时,全部跳转均过此拦截器;默认为false,当为false时须要设置在@HMRouter的interceptors中才见效。
实行时机:
在路由栈发生变化前,转场动画发生前进行回调。
拦截器实行顺序:

  • 按照优先级顺序实行,不区分自定义或者全局拦截器,优先级雷同时先实行@HMRouter中定义的自定义拦截器
  • 当优先级一致时,先实行srcPage>targetPage>global
   srcPage表现跳转发起页面。
  targetPage表现跳转结束时展示的页面。
  示例:
  1. @HMInterceptor({
  2.   priority: 9,
  3.   interceptorName: 'LoginInterceptor'
  4. })
  5. export class LoginInterceptor implements IHMInterceptor {
  6.   handle(info: HMInterceptorInfo): HMInterceptorAction {
  7.     if (isLogin) {
  8.       // 跳转下一个拦截器处理
  9.       return HMInterceptorAction.DO_NEXT;
  10.     } else {
  11.       HMRouterMgr.push({
  12.         pageUrl: 'loginPage',
  13.         param: { targetUrl: info.targetName },
  14.         skipAllInterceptor: true
  15.       })
  16.       // 拦截结束,不再执行下一个拦截器,不再执行相关转场和路由栈操作
  17.       return HMInterceptorAction.DO_REJECT;
  18.     }
  19.   }
  20. }
复制代码
生命周期标签 @HMLifecycle

@HMLifecycle(lifecycleName, priority, global)
标记在实现了IHMLifecycle的对象上,声明此对象为一个自定义生命周期处置处罚器


  • lifecycleName: string, 自定义生命周期处置处罚器名称,必填
  • priority: number, 生命周期优先级,数字越大优先级越高,非必填,默认为9;
  • global: boolean, 是否为全局生命周期,当设置为true时,全部页面生命周期事件会转发到此对象;默认为false
生命周期触发顺序:
按照优先级顺序触发,不区分自定义或者全局生命周期,优先级雷同时先实行@HMRouter中定义的自定义生命周期
示例:
  1. @HMLifecycle({ lifecycleName: 'exampleLifecycle' })
  2. export class ExampleLifecycle implements IHMLifecycle {
  3. }
复制代码
转场动画标签 @HMAnimator

标记在实现了IHMAnimator的对象上,声明此对象为一个自定义转场动画对象


  • animatorName: string, 自定义动画名称,必填。
示例:
  1. @HMAnimator({ animatorName: 'exampleAnimator' })
  2. export class ExampleAnimator implements IHMAnimator {
  3.   effect(enterHandle: HMAnimatorHandle, exitHandle: HMAnimatorHandle): void {
  4.   }
  5. }
复制代码
服务标签 @HMService

标记在类的方法上,声明此方法为一个服务


  • serviceName: string,服务名称,必填。
  • singleton: boolean,是否是单例,非必填,默认为false
示例:
  1. export class ExampleClass {
  2.   @HMService({ serviceName: 'ExampleService', singleton: true })
  3.   exampleFun(params: string): void {
  4.   }
  5. }
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

王國慶

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表