ohrouter:基于 OpenHarmony 的应用内页面路由框架项目

一款功能强大的路由框架,聚焦应用内原生页面的跳转,帮助开发者更好的进行模块间解耦。底层对系统Navigation进行封装,提供了可复用的路由拦截、页面生命周期、自定义转场动画,并且在传参、额外的生命周期、服务型路由方面对系统能力进行了扩展。

分支1Tags2
文件最后提交记录最后更新时间
9 个月前
1 年前
26 天前
28 天前
10 小时前
28 天前
10 小时前
2 个月前
3 个月前
10 小时前
1 年前
1 年前
1 年前
1 年前
1 年前
2 个月前
10 个月前
7 个月前
3 个月前
7 个月前
9 个月前
8 个月前

HMRouter

HMRouter作为应用内页面跳转场景解决方案,为开发者提供了功能完备、高效易用的路由框架。

HMRouter底层对系统Navigation进行封装,集成了NavigationNavDestinationNavPathStack的系统能力,提供了可复用的路由拦截、页面生命周期、自定义转场动画,并且在跳转传参、额外的生命周期、服务型路由方面对系统能力进行了扩展,同时开发者可以高效的将历史代码中的Navigation组件接入到HMRouter框架中。

目的是让开发者在开发过程中减少模板代码,降低拦截器、自定义转场动画、组件感知页面生命周期等高频开发场景的实现复杂度,帮助开发者更好的实现路由与业务模块间的解耦。

特性

  • 基于注解声明路由信息(普通页面、Dialog页面、单例页面)
  • 注解参数支持使用字符串常量定义
  • 页面路径支持正则匹配
  • 支持在Har、Hsp、Hap中使用
  • 支持Navigation路由栈嵌套
  • 支持服务型路由
  • 跳转时支持标准URL解析
  • 支持路由拦截器(包含全局拦截、单页面拦截、跳转时一次性拦截)
  • 支持生命周期回调(包含全局生命周期、单页面生命周期、NavBar生命周期)
  • 内置转场动画(普通页面、Dialog),支持交互式转场动画,同时支持配置某个页面的转场动画、跳转时的一次性动画
  • 提供更多高阶转场动画,包括一镜到底等(需依赖@hadss/hmrouter-transitions
  • 支持配置自定义页面模版,可以更灵活的生成页面文件
  • 支持混淆白名单自动配置
  • 支持与系统Navigation/NavDestination组件混用

依赖系统版本

  • 编译工程要求的SDK版本: Ohos_sdk_public 6.0.0 (API 20 Release)及以上
  • 应用运行要求的SDK版本: Ohos_sdk_public 5.0.0 (API 12 Release)及以上

快速开始

1. 安装依赖

使用 ohpm 安装

# 安装路由框架核心库
ohpm install @hadss/hmrouter

# 如需高级转场动画,安装转场动画库
ohpm install @hadss/hmrouter-transitions

2. 配置编译插件

依赖配置

插件版本建议和库的版本保持一致

修改工程根目录下的hvigor/hvigor-config.json 文件,加入路由编译插件

{
  "dependencies": {
    "@hadss/hmrouter-plugin": "latest"  // 使用npm仓版本号
  },
  // ...其余配置
}

插件配置

修改工程根目录下的hvigorfile.ts,使用路由编译插件

// 工程根目录/hvigorfile.ts
import { appTasks } from '@ohos/hvigor-ohos-plugin';
import { appPlugin } from "@hadss/hmrouter-plugin";

export default {
  system: appTasks,
  plugins: [appPlugin({ ignoreModuleNames: [ /** 不需要扫描的模块 **/ ] })]
};

模块中单独配置,使用modulePlugin()

插件配置详情请参考插件使用说明

3. 初始化路由框架

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

export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    // 日志开启需在init之前调用,否则会丢失初始化日志
    HMRouterMgr.openLog("INFO")
    HMRouterMgr.init({
      context: this.context
    })
  }
}

使用启动框架请查看:如何在启动框架中初始化HMRouter

4. 定义路由入口

HMRouter 依赖系统 Navigation 能力,所以必须在页面中定义一个 HMNavigation 容器,并设置相关参数,具体代码如下:

@Entry
@Component
export struct Index {
  modifier: MyNavModifier = new MyNavModifier();

  build() {
    // @Entry中需要再套一层容器组件,Column或者Stack
    Column(){
      // 使用HMNavigation容器
      HMNavigation({
        navigationId: 'MainNavigation', homePageUrl: 'HomePage',
        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设置

5. 页面定义与路由跳转

使用 @HMRouter 标签定义页面,绑定拦截器、生命周期及自定义转场动画

@HMRouter({
  pageUrl: 'PageB',
  interceptor: ['PageInterceptor'],
  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.urlParam?.get('msg')}`)
    }
  }
}

定义页面 HomePage,使用 HMRouterMgr.push 执行路由跳转至 PageB

const PAGE_URL: string = 'HomePage'

@HMRouter({ pageUrl: PAGE_URL })
@Component
export struct HomePage {
  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的参数内容

6. 定义拦截器

使用 HMInterceptor 定义拦截器,并实现IHMInterceptor接口

@HMInterceptor({ interceptorName: 'PageInterceptor' })
export class PageInterceptor 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;
    }
  }
}

7. 定义生命周期

组件感知页面生命周期

通过 addObserver 接口,组件可以感知页面的生命周期事件:

@Component
struct ChildComponent {
  @State backPressCount: number = 0;
  private lifecycleOwner = HMRouterMgr.getCurrentLifecycleOwner();
  private handleCallback = () => {
    this.showToast();
    this.backPressCount++;
    return true;
  }

  aboutToAppear(): void {
    this.lifecycleOwner?.addObserver(HMLifecycleState.onBackPressed, this.handleCallback);
  }

  aboutToDisappear(): void {
    this.lifecycleOwner?.removeObserver(HMLifecycleState.onBackPressed, this.handleCallback);
  }

  // 组件内定义的方法
  showToast() {
    this.getUIContext().getPromptAction().showToast({ message: RouterPageConstant.LIFECYCLE_CASE1_TOAST });
  }

  build() {
    // UI内容
  }
}

页面绑定生命周期

使用@HMLifecycle标签定义生命周期处理器,并实现IHMLifecycle接口,页面可在 @HMRouter 注解中通过 lifecycle 属性来绑定

@HMLifecycle({ lifecycleName: 'PageLifecycle' })
export class PageLifecycle implements IHMLifecycle {
  model: ObservedModel = new ObservedModel()
  private time: number = 0;

  onShown(ctx: HMLifecycleContext): void {
    this.time = new Date().getTime();
  }

  onHidden(ctx: HMLifecycleContext): void {
    const duration = new Date().getTime() - this.time;
    console.info(`Page ${ctx.navContext?.pathInfo.name} stay ${duration}`);
  }
}

8. 定义转场动画

通过@HMAnimator标签定义转场动画,并实现IHMAnimator接口

@HMAnimator({ animatorName: 'PageAnimator' })
export class PageAnimator implements IHMAnimator {
  effect(enterHandle: HMAnimatorHandle, exitHandle: HMAnimatorHandle): void {
    // 入场动画
    enterHandle.start((modifier: AttributeUpdater<NavDestinationAttribute>) => {
      modifier.attribute?.translate({ y: "100%" }).opacity(0.4);
    }).finish((modifier: AttributeUpdater<NavDestinationAttribute>) => {
      modifier.attribute?.translate({ y: "0" }).opacity(1);
    }).onFinish((modifier: AttributeUpdater<NavDestinationAttribute>) => {
      modifier.attribute?.translate({ y: "0" }).opacity(1);
    });

    // 出场动画
    exitHandle.start((modifier: AttributeUpdater<NavDestinationAttribute>) => {
      modifier.attribute?.translate({ y: "0" }).opacity(1);
    }).finish((modifier: AttributeUpdater<NavDestinationAttribute>) => {
      modifier.attribute?.translate({ y: "100%" }).opacity(0.4);
    }).onFinish((modifier: AttributeUpdater<NavDestinationAttribute>) => {
      modifier.attribute?.translate({ y: "0" }).opacity(1);
    });
  }
}

自定义转场动画的详细使用:查看详情

9. 服务路由使用

服务路由用于类似服务提供发现机制(Service Provider Interface),通过不依赖实现模块的方式获取接口实例并调用方法,可以直接获取服务实例对象,也可以直接进行方法级服务调用

// 方法级服务
export class CustomService {
  @HMService({ serviceName: 'testFunWithReturn' })
  testFunWithReturn(param1: string, param2: string): string {
    return `调用服务 testFunWithReturn:${param1} ${param2}`
  }
}

// 定义服务接口
interface IDataService {
  fetchData(page: number, size: number): Promise<Object[]>;
}
// 实现服务提供者
@HMServiceProvider({ serviceName: 'DataService', singleton: true })
export class DataServiceImpl implements IDataService {
  async fetchData(page: number, size: number): Promise<Object[]> {
    // 实现数据获取逻辑
    return [];
  }
}

@HMRouter({ pageUrl: 'test://MainPage' })
@Component
export struct Index {

  build() {
    Row() {
      Column({ space: 8 }) {
        Button('service').onClick(() => {
          // 使用getService拿到实例对象
          let data = HMRouterMgr.getService<IDataService>('DataService').fetchData();
          // 直接调用方法级服务
          Logger.info(HMRouterMgr.request('testFunWithReturn', 'home', 'service').data)
        })
      }
      .width('100%')
    }
    .height('100%')
  }
}

应用内页面跳转场景解决方案

查看详情

与系统Navigation/NavDestination组件混用说明

查看详情

混淆说明

查看详情

API参考

查看详情

插件使用说明

查看详情

兼容性矩阵

HMRouter运行库
查看日志
映射 HMRouter动效库
查看日志
映射 HMRouter插件
查看日志
1.2.4(推荐版本) 1.2.4(推荐版本) 1.2.4(推荐版本)
1.2.3 1.2.3 1.2.3
1.2.3-rc.1 1.2.3-rc.1 1.2.3-rc.1
1.2.3-rc.0 1.2.3-rc.0 1.2.3-rc.0
1.2.2 1.2.2 1.2.2
1.2.0 1.2.0 1.2.0
1.2.0-rc.0 1.2.0-rc.0 1.2.0-rc.0
1.2.0-beta.1 1.2.0-beta.1 1.2.0-beta.1
1.2.0-beta.0 1.2.0-beta.0 1.2.0-beta.0
1.0.0-rc.10 1.0.0-rc.10 1.0.0-rc.9

FAQ

查看详情

Wiki

查看详情

Sample参考

原理介绍

查看详情

贡献代码

使用过程中发现任何问题都可以提 Issue ,当然,也非常欢迎发 PullRequest 共建。

开源协议

本项目基于 Apache License 2.0 ,请自由地享受和参与开源。

项目介绍

一款功能强大的路由框架,聚焦应用内原生页面的跳转,帮助开发者更好的进行模块间解耦。底层对系统Navigation进行封装,提供了可复用的路由拦截、页面生命周期、自定义转场动画,并且在传参、额外的生命周期、服务型路由方面对系统能力进行了扩展。

定制我的领域