场景:多har包之间的页面跳
A.har中的A需要跳转到B.har的B中,由于A.har与B.har之间由不同业务开发,互相隔离。如果A.har要跳转到B.har,必然需要配置依赖,引入B.har。如果B.har开发不完善或存在运行bug,就必然影响互相开发效率。
如下图所示:
鸿蒙官方给的解决方案:提升两个包之间的互相依赖到另一个共用路由模块中。如果对android开发熟悉的小伙伴,可能想到了jetpack中同名的库。依赖方案如下图所示:
逻辑实现
路由模块的实现
RouterModule模块包括:全局路由栈(NavPathStack)和路由表信息(Map)。
- 路由栈及路由表
typescript
export class RouterModule {
// WrapperBuilder 支持@Builder描述的组件,以参数形式进行封装存储
static buildMap: Map<string, WrapperBuilder<[object]>> = new Map<string, WrapperBuilder<[object]>>()
// 路由栈,会关联Navigtion组件
static navPathStack: NavPathStack = new NavPathStack()
}
- 注册页面组件到路由表,以及获取路由页面
csharp
export class RouterModule {
public static registerBuilder(builderName: string, builder: WrapperBuilder<[object]>): void {
RouterModule.buildMap.set(builderName, builder)
}
public static getBuilder(builderName: string): WrapperBuilder<[object]> {
const builder = RounterModule.builderMap.get(builderName)
if (!builder) {
// not found builder
}
return builder as WrapperBuilder<[object]>
}
}
- 路由跳转
typescript
export class RouterModule {
// 通过获取页面栈跳转到指定页面
public static async push(harName: string, builderName: string): Promise<void> {
// 动态导入页面所在har模块,避免业务har模块中显示依赖其他har模块,实现按需加载
await import(harName);
// 通过路由栈的方法,按路由名字进行路由跳转
RouterModule.navPathStack.pushPathByName(builderName, null);
}
}
补充:个人理解,这个模块应该算是Navigation的组件模块之一,但不知道为什么需要开发者自己去实现。
页面跳转实现
- 工程主模块加入RouterModule模块,和需要路由的har模块
scss
// Entry.hap中的oh-package.json5文件
"dependencies": {
"@ohos/routermodule": "file:../RouterModule",
"@ohos/hara": "file:../harA",
"@ohos/harb": "file:../harB"
}
- 由主工程关联上路由
@Component
struct EntryHap {
@State entryHapARouter: NavPathStack = new NavPathStack();
aboutToAppear() {
if (!this.entryHapARouter) {
this.entryHapARouter = new NavPathStack();
}
RouterModule.createRouter('EntryHapA_Router', this.entryHapARouter);
};
@Builder
routerMap(builderName: string, param: object) {
// 从RouterModule中获取全局路由表
RouterModule.getBuilder(builderName).builder(param);
}
build() {
// 绑定RouterModule中路由栈
Navigation(RouterModule.navPathStack) {
// ...
}
.navDestination(this.routerMap); // 从RouterModule中获取全局路由表
}
}
- 在harB中声明需要跳转的页面,并且调用registerBuilder接口将页面注册到RouterModule模块的全局路由表上。以下注册逻辑会在harB的B1页面被首次加载时触发。
scss
// harB模块的B1页面
@Builder
export function harBuilder(value: object) {
NavDestination() {
Column() {
// ...
}
}
}
// 在页面首次加载时触发执行
let builderName = 'B1';
// 避免重复注册
if (!RouterModule.getBuilder(builderName)) {
// 通过系统提供的wrapBuilder接口封装@Builder装饰的方法,生成harB1页面builder
let builder: WrappedBuilder<[object]> = wrapBuilder(harBuilder);
// 注册harB1页面到全局路由表
RouterModule.registerBuilder(builderName, builder);
}
- 动态加载,harB对外提供导出类index.ets,定义加载初始化函数。
javascript
export function harInit(name: string): void {
// 根据routerModule中路由表的key值动态加载要跳转的页面的相对路径
switch (name) {
case "B1":
import("./src/main/ets/components/mainpage/B1");
break;
case "B2":
import("./src/main/ets/components/mainpage/B2");
break;
case "B3":
import("./src/main/ets/components/mainpage/B3");
break;
default:
break;
}
}
- 在harA模块中的A1页面调用RouterModule模块的push方法实现跳转到harB的B1页面。当harB的B1页面被首次通过push方法跳转时,会动态加载B1页面,并且触发步骤3中B1页面的路由注册逻辑,把B1页面注册到RouterModule的全局路由表builderMap中
scss
@Builder
export function harBuilder(value: object) {
NavDestination() {
Column() {
Button($r("app.string.to_harb_page1"), { stateEffect: true, type: ButtonType.Capsule })
.onClick(() => {
// 首次调用B1的push时,会动态加载B1页面并且触发页面注册逻辑
RouterModule.push('@ohos/harb/src/main/ets/components/mainpage/B1', 'B1');
})
}
.width('100%')
.height('100%')
}
.title('A1Page')
.onBackPressed(() => {
RouterModule.pop(RouterNameConstants.ENTRY_HAP);
return true;
})
}
说明:步骤3中是在builder中才完成页面的注册,而在步骤4的注册阶段这里,调用push时,采用动态加载的页面加载的方式,触发了builder方法,完成了注册。
注意:route方案将会被官方放弃,页面之间的路由方案,请优先选择Navigation。