HarmonyOS--Navigation跨模块页面路由实现模块间解耦

概述

我们通过模块化设计以及分层设计来设计一个应用,这里不免会遇到一些问题比如说,你需要在模块a的页面a,跳转到模块b的页面a,就好比当你发现用户没有登录的时候,你需要在首页模块的第一个页面跳转到登录模块的登录页面

那么最基础的实现方法就是两个模块相互依赖后,导入该页面。

跨模块路由基本实现

在harA模块中的A1页面组件中开发Navigation组件,并关联与之对应的NavPathStack路由栈,示例代码如下:

@Component
struct A1 {
  // 创建NavPathStack路由栈对象
  @State harARouter: NavPathStack = new NavPathStack();

  build() {
    // Navigation组件关联NavPathStack对象
    Navigation(this.harARouter) {
      // ...
    }
  }
}

在harA模块的oh-package.json5文件中添加harB模块的依赖,并且把harB模块中需要跳转的B1组件添加到harA模块的Navigation组件路由表中,示例代码如下

"dependencies": {
  // 添加对harB的依赖
  "@ohos/harb": "file:../harB"
}

在harA模块的A1组件中的routerMap路由表中,添加harB模块的B1组件,示例代码如下:

import { B1 } from '@ohos/harb';
struct A1 {
  @State harARouter: NavPathStack = new NavPathStack();

  @Builder
  routerMap(builderName: string, param: object) {
    if (builderName === 'B1') {
      B1() // 在routerMap中添加需要跳转的harB模块的B1页面
    }
  }
  
  build() {
    Navigation(this.harARouter) {
      // ...
    }
    .navDestination(this.routerMap) // Navigation关联上routerMap路由表
  }
}

在harA模块的Navigation组件中添加跳转到harB模块的B1页面的逻辑,完整示例代码如下:

import { B1 } from '@ohos/harb';

struct A1 {
  // 创建NavPathStack路由栈
  @State harARouter: NavPathStack = new NavPathStack();

  @Builder
  routerMap(builderName: string, param: object) {
    if (builderName === 'B1') {
      B1() // 在routerMap中添加需要跳转的harB模块的B1页面
    }
  }

  build() {
    // Navigation关联NavPathStack对象
    Navigation(this.harARouter) {
      Button('跳转到HarB的B1页面')
        .onClick(() => {
          // 跳转到已在路由表注册的harB模块的B1页面
          this.harARouter.pushPathByName('B1', null);
        })
    }
    .navDestination(this.routerMap) // Navigation关联上routerMap路由表
  }
}

当前基本方案主要存在以下问题:

使用Navigation时,所有路由页面需要主动通过import方式逐个导入当前页面,并存入页面路由表routerMap中。
主动使用import的方式需显性指定加载路径,造成开发态模块耦合严重。
模块无法独立编译,且存在开发态模块间循环依赖问题。

实现模块间解耦的方案

为了解决上述的问题,我们需要封装一个RouterModuel模块,提供存储页面路由表,页面栈,来实现页面间的动态跳转

第一步:实现模块功能

import { RouterModel } from "../model/RouterModel"

export class RouterModule{
  //路由表创建
  static builderMap:Map<string,WrappedBuilder<[object]>>=new Map<string,WrappedBuilder<[object]>>()
  //路由栈的创建
  static routerMap:Map<string,NavPathStack>=new Map<string,NavPathStack>()

  //注册页面名称和页面构建函数
  public static registerBuilder(builderName:string,builder:WrappedBuilder<[object]>){
    RouterModule.builderMap.set(builderName,builder)
  }

  //通过名称获取目标页面
  public static getBuilder(builderName:string):WrappedBuilder<[object]>{
    const builder=RouterModule.builderMap.get(builderName)
    if(!builder){
      console.log('没有找到页面:'+builderName)
    }
    return builder as WrappedBuilder<[object]>
  }

  //注册路由栈
  public static registerRouter(routerName:string,router:NavPathStack){
    RouterModule.routerMap.set(routerName,router)
  }

  //根据名称获取路由栈
  public static getRouter(routerName:string):NavPathStack{
    return RouterModule.routerMap.get(routerName) as NavPathStack
  }

  //跳转到指定页面
  public static async push(router:RouterModel){
    //获取需要跳转的路由页面所在的包名
    const harName=router.builderName.split('_')[0]
    console.log('跳转模块:'+harName)
    //动态导入该页面
    await import(harName).then((ns:ESObject)=>{ns.harInit(router.builderName)})
    RouterModule.getRouter(router.routerName).pushPath({name:router.builderName,param:router.param})
  }

  //弹出当前页面
  public static pop(routerName: string): void {
    RouterModule.getRouter(routerName).pop();
  }

  //清空当前页面栈
  public static clear(routerName: string): void {
    RouterModule.getRouter(routerName).clear();
  }

  //弹出指定页面
  public static popToName(routerName: string, builderName: string): void {
    RouterModule.getRouter(routerName).popToName(builderName);
  }
}

需要提供的必要信息如下

import { RouterModule } from "../../../../Index"

//路由基本信息
export class RouterModel{
  //需要跳转的页面名称
  //注意这里有特定格式:包名_页面名称 如:login_loginpage
  builderName:string=''
  //需要操作的页面栈
  routerName:string=''
  //需要传递的信息
  param?:object=new Object
}

//通过获取路由基本信息进行页面的跳转
export function builderRouterModel(routerName:string,builderName:string,param?:object){
  let router:RouterModel=new RouterModel()
  router.builderName=builderName
  router.routerName=routerName
  router.param=param
  RouterModule.push(router)
}

一定要注意builderName的格式,这里和跳转函数息息相关

const harName=router.builderName.split('_')[0]
//获取跳转的包名
    console.log('跳转模块:'+harName)
    //动态导入该页面
    //这里相当于找到包名后,再去根据名称找到该模块下的目标页面并import实现动态加载
    await import(harName).then((ns:ESObject)=>{ns.harInit(router.builderName)})
    RouterModule.getRouter(router.routerName).pushPath({name:router.builderName,param:router.param}

该模块提供的功能如下:

  1. 通过键值对的方式存储路由表,路由栈,并提供函数通过Key查找对应的路由页面和栈
  2. 提供跳转方法,参数是跳转用到的路由栈,路由页面以及要传递的路由信息
  3. 总体实现的功能就是,存储页面栈,存储各个参与路由的页面,然后根据提供的页面栈名称给出对应的页面栈,然后根据提供的页面名称去动态导入该页面所在的包以及路径,再进行跳转

目标页面的路由注册

const builderName = BuilderNameConstants.LOGIN_LOGINPAGE;
if (!RouterModule.getBuilder(builderName)) {
  const builder: WrappedBuilder<[object]> = wrapBuilder(loginPage);
  RouterModule.registerBuilder(builderName, builder);
  console.log('注册页面成功:'+builderName)
}

这段代码跟在目标页面下方,当页面首次加载的时候调用

加载函数

import { BuilderNameConstants } from 'routermodule';

export { loginPage } from './src/main/ets/view/loginPage'

export function harInit(builderName:string){
  switch (builderName){
    case BuilderNameConstants.LOGIN_LOGINPAGE:
      import('./src//main/ets/view/loginPage')
    break;
      case BuilderNameConstants.LOGIN_LOGINFEATURE:
        import('./src/main/ets/view/LoginFeature')
    break;
      default :
        break;
  }
}

这段代码一般放在HAR包的Index文件里,根据页面名称提供对应的文件路径

总结

本篇文章讲述了实现模块间解耦的跨模块路由方法,具体是怎么解耦的呢?

按照基本方法,你需要在模块A的a页面跳转到模块B的a页面,你需要在模块A添加依赖,然后导入目标页面到routeMap里进行路由,这样会导致模块间可能出现循环依赖,而且高耦合。

而本文介绍一种方法,创建一个路由模块,收集各个模块的路由信息和路径,并且提供跳转方法,你只需要注册路由栈,注册模块A的a页面和模块B的a页面,然后该模块会帮助你实现动态的导入。相当于模块A和模块B不再相互依赖,而是同时依赖路由模块,由路由模块实现路由操作,实现解耦。

在这里插入图片描述

实现代码及文章

实现代码
官网文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值