概述
我们通过模块化设计以及分层设计来设计一个应用,这里不免会遇到一些问题比如说,你需要在模块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}
该模块提供的功能如下:
- 通过键值对的方式存储路由表,路由栈,并提供函数通过Key查找对应的路由页面和栈
- 提供跳转方法,参数是跳转用到的路由栈,路由页面以及要传递的路由信息
- 总体实现的功能就是,存储页面栈,存储各个参与路由的页面,然后根据提供的页面栈名称给出对应的页面栈,然后根据提供的页面名称去动态导入该页面所在的包以及路径,再进行跳转
目标页面的路由注册
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不再相互依赖,而是同时依赖路由模块,由路由模块实现路由操作,实现解耦。