介绍
Navigation动态路由运用了WrapperBuilder的自定义函数打包能力,以及动态路由的跨模块文件引用的能力,解除了har包和hap包的依赖关系,实现了 即使hap包不引用har包,依然能跳转到har包中的页面的能力 目前还不支持动态import变量表达式和跨模块相对路径的文件,所以代码中使用switch作为替代。(官方还有一个自定义路由表,由于涉及到自定义装饰器,等有机会在讲)
使用说明
1.创建hapA harA harB
2.创建路由框架RouterModule,使用map存储hap包的路由和har包的页面信息
3.在RouterModule中封装get和set方法,并对外开放,允许外部模块引用和调用
4.创建push方法,允许传入一串url,并对其进行解析,通过拆解出路由名称从map中获取到路由栈,并将目标页面压栈
5.在hap和har包中引入RouterModule,将hap包的路由栈和har包的页面信息通过开放的set方法存入RouterModule
工程目录
├──entry // 入口模块
│ ├──build-profile.json5 // 编译配置文件,其中arkOptions需配置动态import依赖的包名
│ ├──oh-package.json5 // 依赖配置,需依赖全部子业务模块和RouterModule模块
│ ├──src/main/ets
│ │ ├──entryability
│ │ │ └──EntryAbility.ets
│ │ └──pages
│ │ └──Index.ets // 首页
│ └──src/main/resources // 资源目录
├──harA // 子业务模块
│ ├──Index.ets // 入口文件,对外暴露模块方法
│ ├──oh-package.json5 // 依赖配置,需依赖RouterModule模块
│ ├──src/main/ets/components/mainpage
│ │ ├──A1.ets
│ │ └──A2.ets
│ └──src/main/resources
├──harB // 子业务模块
│ ├──Index.ets // 入口文件,对外暴露模块方法
│ ├──oh-package.json5 // 依赖配置,需依赖RouterModule模块
│ ├──src/main/ets/components/mainpage
│ │ ├──B1.ets
│ │ ├──B2.ets
│ │ └──B3.ets
│ └──src/main/resources
├──harC // 子业务模块
│ ├──Index.ets // 入口文件,对外暴露模块方法
│ ├──oh-package.json5 // 依赖配置,需依赖RouterModule模块
│ ├──src/main/ets/components/mainpage
│ │ ├──C1.ets
│ │ └──C2.ets
│ └──src/main/resources
└──RouterModule // 路由模块
├──Index.ets // 入口文件,对外暴露路由的方法和常量
├──oh-package.json5
├──src/main/ets/constants // 路由信息常量
│ └──RouterConstants.ets
├──src/main/ets/model // 路由信息模型
│ └──RouterModel.ets
├──src/main/ets/utils // 对外提供的路由方法
│ └──RouterModule.ets
└──src/main/resources
具体实现
RouterModule模块
RouterModule
//RouterModule工具类
import { RouterModel } from '../model/RouterModel';
import Logger from './Logger';
export class RouterModule {
//用来存放wrapbuilder 一个wrapbuilder对应一个navDestination
static builderMap: Map<string, WrappedBuilder<[object]>> = new Map<string, WrappedBuilder<[object]>>();
//用来存放路由栈的 一个Navigation会有一个NavPathStack 如果项目只需要一个Navigation不需要这一步
static routerMap: Map<string, NavPathStack> = new Map<string, NavPathStack>();
// 把一个navDestination存放在map里面
public static registerBuilder(builderName: string, builder: WrappedBuilder<[object]>): void {
RouterModule.builderMap.set(builderName, builder);
}
// 通过名字获取到对应的navDestination
public static getBuilder(builderName: string): WrappedBuilder<[object]> {
const builder = RouterModule.builderMap.get(builderName);
if (!builder) {
Logger.info('not found builder ' + builderName);
}
return builder as WrappedBuilder<[object]>;
}
// 把一个navpathstack存放在map里面
public static createRouter(routerName: string, router: NavPathStack): void {
RouterModule.routerMap.set(routerName, router);
}
//通过名字获取到对应的navpathstack
public static getRouter(routerName: string): NavPathStack {
return RouterModule.routerMap.get(routerName) as NavPathStack;
}
// 通过RouterModel跳到对应的NavDestination页面 (RouterMoudel会在下面讲)
public static async push(router: RouterModel): Promise<void> {
//切割出来har包名字 因为BuilderName我们的命名规则是'@ohos/hara_A1' 这样
const harName = router.builderName.split('_')[0];
// 不懂这里面为什么await加上.then 但是不这么写就会报错
// 加载har名字 在对应har里面index设置动态加载页面组件的接口harInit,这边下面会有对应的代码
await import(harName).then((ns: ESObject): Promise<void> => ns.harInit(router.builderName));
// 通过获取的路由栈 进行push到相关的NavDestination里面
RouterModule.getRouter(router.routerName).pushPath({ name: router.builderName, param: router.param });
}
// 返回到上一个页面 相当于router.back
public static pop(routerName: string): void {
// Find the corresponding route stack for pop.
RouterModule.getRouter(routerName).pop();
}
//清除页面栈
public static clear(routerName: string): void {
// Find the corresponding route stack for pop.
RouterModule.getRouter(routerName).clear();
}
//返回到对应的NavDestination上
public static popToName(routerName: string, builderName: string): void {
RouterModule.getRouter(routerName).popToName(builderName);
}
}
RouterModel
//RouterModel
import { RouterModule } from '../utils/RouterModule';
//一个NavDestination 一个对应的Navpathstack 一个传的参数
export class RouterModel {
builderName: string = "";
routerName: string = "";
//传的param
param?: object = new Object();
}
//把routermodel传到push里面
export function buildRouterModel(routerName: string, builderName: string, param?: object) {
let router: RouterModel = new RouterModel();
router.builderName = builderName;
router.routerName = routerName;
router.param = param;
RouterModule.push(router);
}
RouterConstants
//这个页面没什么东西 就是index对应的harinit
export class BuilderNameConstants {
//我们在上面的RouterModule里面做了一个slice('_')[0]的操作 目的就是为了把har包的依赖名字切出来
static readonly HARA_A1: string = '@ohos/hara_A1';
static readonly HARA_A2: string = '@ohos/hara_A2';
static readonly HARB_B1: string = '@ohos/harb_B1';
static readonly HARB_B2: string = '@ohos/harb_B2';
static readonly HARB_B3: string = '@ohos/harb_B3';
static readonly HARC_C1: string = '@ohos/harc_C1';
static readonly HARC_C2: string = '@ohos/harc_C2';
}
export class RouterNameConstants {
static readonly ENTRY_HAP: string = 'EntryHap_Router';
}
// 这个是跳转到的har包需要配置的index 因为怕看不懂在这个代码块写
在子模块中添加动态加载页面组件的接口harInit,其中pageName和RouterInfo中配置的pageName相同,import()接口中传入的参数,是页面的相对路径。详细代码可参考Index.ets。 如果模块中有多个页面需要跳转,则需要配置多个pageName和页面路径,并且pageName和页面路径需要一一对应,否则无法跳转到预期中的页面
import { BuilderNameConstants } from '@ohos/routermodule';
export function harInit(builderName: string): void {
// 动态引入要跳转的页面
switch (builderName) {
//通过对应的builderNmae跳转到对应的页面
case BuilderNameConstants.HARA_A1:
import("./src/main/ets/components/mainpage/A1");
break;
case BuilderNameConstants.HARA_A2:
import("./src/main/ets/components/mainpage/A2");
break;
default:
break;
}
}
Entry模块
import { BuilderNameConstants, buildRouterModel, RouterModule, RouterNameConstants } from '@ohos/routermodule';
@Entry
@Component
struct EntryHap {
@State entryHapRouter: NavPathStack = new NavPathStack();
aboutToAppear() {
if (!this.entryHapRouter) {
this.entryHapRouter = new NavPathStack();
}
//初始化路由比哦啊哦
RouterModule.createRouter(RouterNameConstants.ENTRY_HAP, this.entryHapRouter);
};
@Builder
routerMap(builderName: string, param: object) {
RouterModule.getBuilder(builderName).builder(param);
};
build() {
Navigation(this.entryHapRouter) {
Button($r("app.string.to_hara_pageA1"), { stateEffect: true, type: ButtonType.Capsule })
.width('80%')
.height(40)
.margin(20)
.onClick(() => {
//这个方法里面有push,第一个参数的路由栈的名字 第二个参数的跳转的NavDestinatio的名字
buildRouterModel(RouterNameConstants.ENTRY_HAP, BuilderNameConstants.HARA_A1, new Object({
origin: 'Entry'
}));
})
}
.title('NavIndex')
//创建NavDestination组件。使用builder函数,基于name和param构造NavDestination组件。builder下只能有一个根节点。builder中允许在NavDestination组件外包含一层自定义组件, 但自定义组件不允许设置属性和事件,否则仅显示空白。
.navDestination(this.routerMap);
}
}
HAR_A模块
import { BuilderNameConstants, buildRouterModel, RouterModule, RouterNameConstants, } from '@ohos/routermodule';
@Builder
export function harBuilder(value: object) {
NavDestination() {
Column() {
Text(`传入的参数:${JSON.stringify(value)}`)
.margin(20)
Button($r("app.string.to_index"), { stateEffect: true, type: ButtonType.Capsule })
.width('80%')
.height(40)
.margin(20)
.onClick(() => {
RouterModule.clear(RouterNameConstants.ENTRY_HAP);
})
}
.width('100%')
.height('100%')
}
.title('A1Page')
.onBackPressed(() => {
RouterModule.pop(RouterNameConstants.ENTRY_HAP);
return true;
})
}
const builderName = BuilderNameConstants.HARA_A1;
//如果map里面找不到对应的builder就把他存起来
if (!RouterModule.getBuilder(builderName)) {
//把上面的全局builder存在wrapBuilder里面
const builder: WrappedBuilder<[object]> = wrapBuilder(harBuilder);
RouterModule.registerBuilder(builderName, builder);
}
这就实现了基本的动态路由
参考代码