HarmonyOS Navigation动态import
目录
一、核心概念(一句话概括)
Navigation动态路由实现了**跨模块页面跳转**,即使HAP包不直接依赖HAR包,也能动态跳转到HAR包中的页面,实现模块解耦。
二、实现步骤(按步骤说明)
第一步:创建路由管理模块(RouterModule)
使用两个Map存储:
- `builderMap`:存储页面构建器
- `routerMap`:存储路由栈
- 提供注册、获取、跳转等方法
第二步:主页面(HAP包)初始化
1. **创建NavPathStack并注册**
aboutToAppear() {
this.hapARouter = new NavPathStack();
RouterModule.createRouter("HapA_Router", this.hapARouter);
}
2. **使用Navigation组件,绑定动态路由映射**
Navigation(this.hapARouter) {
// 内容
}
.navDestination(this.routerMap)
3. **创建路由映射Builder**
@Builder
routerMap(builderName: string, param: object) {
RouterModule.getBuilder(builderName).builder(param);
}
第三步:HAR包页面设置
1. **创建页面Builder并注册**
@Builder
export function harBuilder(value: object) {
NavDestination() { /* 页面内容 */ }
}
// 注册
let builder = wrapBuilder(harBuilder);
RouterModule.registerBuilder("HarA_Page1_Builder", builder)
2. **实现harInit函数(动态导入)**
export function harInit(path: string): void {
switch (path) {
case "./src/main/ets/components/mainpage/page1":
import("./src/main/ets/components/mainpage/page1");
break;
}
}
第四步:执行跳转
- **URL格式**:`{har包名}/{页面路径}/{路由名}-{Builder名}`
- **示例**:`HarA/src/main/ets/components/mainpage/page1/HapA_Router-HarA_Page1_Builder`
- **调用**:`RouterModule.push(url)`
Button("跳转到页面")
.onClick(() => {
let url = "HarA/src/main/ets/components/mainpage/page1/HapA_Router-HarA_Page1_Builder";
RouterModule.push(url);
})
三、跳转原理(push方法执行流程)
1. **解析URL**:拆分出HAR包名、路由名、Builder名
2. **动态导入**:`await import(harName)` 加载HAR包
3. **初始化页面**:调用 `harInit(path)` 加载具体页面模块
4. **执行跳转**:通过路由栈 `pushPathByName(builderName)` 跳转
public static async push(url: string): Promise<void>{
// 1. 解析URL
let params = url.split("/");
const harName = params[0]; // HAR包名
let context = params[params.length - 1].split("-");
const routerName = context[0]; // 路由名
const builderName = context[1]; // Builder名
// 2. 构建页面路径
let path = ".";
for (let i = 1; i < params.length - 1; i++) {
path += "/" + params[i];
}
// 3. 动态导入HAR包并初始化
let ns: ESObject = await import(harName);
ns.harInit(path);
// 4. 通过路由栈跳转
RouterModule.getRouter(routerName).pushPathByName(builderName, null);
}
四、核心机制(关键技术点)
1. 动态导入
- 使用 `import()` 动态加载HAR包
- 通过 `harInit()` 函数按需加载具体页面模块
2. WrappedBuilder包装
什么是wrapBuilder
`wrapBuilder()` 是HarmonyOS提供的API,用于将普通的 `@Builder` 函数包装成 `WrappedBuilder` 类型,使其能够在运行时动态调用。
为什么需要wrapBuilder
- **普通Builder的限制**:普通的 `@Builder` 函数是编译时确定的,无法在运行时通过字符串名称动态调用
- **动态路由的需求**:动态路由需要在运行时根据URL中的Builder名称来调用对应的Builder
- **类型转换**:`wrapBuilder()` 将Builder转换为 `WrappedBuilder<[object]>` 类型,支持动态调用
使用方式
// 1. 定义普通的Builder函数
@Builder
export function harBuilder(value: object) {
NavDestination() {
// 页面内容
}
}
// 2. 使用wrapBuilder包装
let builder: WrappedBuilder<[object]> = wrapBuilder(harBuilder);
// 3. 注册到RouterModule
RouterModule.registerBuilder("HarA_Page1_Builder", builder);
调用方式
// 在路由映射Builder中动态调用
@Builder
routerMap(builderName: string, param: object) {
// 通过名称获取WrappedBuilder并调用
RouterModule.getBuilder(builderName).builder(param);
}
关键特性
- **运行时动态调用**:支持通过字符串名称在运行时查找和调用Builder
- **类型安全**:包装后的 `WrappedBuilder` 保持了类型信息
- **参数传递**:支持通过 `builder(param)` 方法传递参数
- **跨模块支持**:可以在不同模块间传递和调用WrappedBuilder
3. 路由栈管理
- 每个路由栈通过名称进行管理
- 支持多个独立的路由栈
4. 跨模块跳转
- HAP包可以跳转到未直接依赖的HAR包页面
- 实现了真正的动态路由
五、wrapBuilder详解
1. wrapBuilder的作用
`wrapBuilder()` 是动态路由实现的关键API,它解决了以下问题:
问题背景
- Navigation组件的 `navDestination` 需要接收一个Builder函数
- 动态路由需要在运行时根据名称动态调用不同的Builder
- 普通的 `@Builder` 函数无法通过字符串名称动态调用
解决方案
通过 `wrapBuilder()` 将Builder包装成 `WrappedBuilder` 类型:
- 可以在Map中存储,通过名称查找
- 支持运行时动态调用
- 保持了Builder的类型安全
2. wrapBuilder的工作流程
普通Builder函数
↓ wrapBuilder()
WrappedBuilder对象
↓ 注册到RouterModule
存储在builderMap中
↓ 通过名称获取
动态调用builder(param)
3. 完整示例
// 步骤1:定义页面Builder
@Builder
export function harBuilder(value: object) {
NavDestination() {
Column() {
Text('页面内容')
}
}
.title('HarA_Page1')
}
// 步骤2:包装并注册
let builderName = 'HarA_Page1_Builder';
if (!RouterModule.getBuilder(builderName)) {
// 使用wrapBuilder包装
let builder: WrappedBuilder<[object]> = wrapBuilder(harBuilder);
// 注册到RouterModule
RouterModule.registerBuilder(builderName, builder);
}
// 步骤3:动态调用(在主页面)
@Builder
routerMap(builderName: string, param: object) {
// 通过名称获取WrappedBuilder
let wrappedBuilder = RouterModule.getBuilder(builderName);
// 调用builder方法执行构建
wrappedBuilder.builder(param);
}
4. 注意事项
1. **必须使用wrapBuilder**:普通的Builder函数无法直接存储到Map中动态调用
2. **类型必须匹配**:`WrappedBuilder<[object]>` 中的泛型参数必须与Builder函数签名匹配
3. **注册时机**:Builder需要在页面模块加载时注册,通常在模块顶层代码中执行
4. **避免重复注册**:使用 `if (!RouterModule.getBuilder(builderName))` 检查避免重复注册
5. wrapBuilder与动态路由的关系
动态路由跳转流程:
1. RouterModule.push(url)
↓
2. 解析URL获取builderName
↓
3. 动态import HAR包
↓
4. HAR包中的页面模块加载,执行Builder注册
↓
5. wrapBuilder包装Builder并注册到RouterModule
↓
6. pushPathByName(builderName) 触发navDestination
↓
7. routerMap通过builderName获取WrappedBuilder
↓
8. 调用WrappedBuilder.builder(param) 渲染页面
六、应用场景
1. **插件化架构**:不同功能模块作为HAR包,主应用动态加载
2. **按需加载**:减少初始包体积,按需加载功能模块
3. **模块解耦**:HAP包与HAR包之间无需编译时依赖
4. **动态配置**:通过配置文件或网络请求动态决定跳转目标
七、注意事项(面试可能问到)
1. 动态import限制:目前不支持动态import变量表达式和跨模块相对路径,需要使用switch语句作为替代方案
2. Builder注册:每个页面的Builder需要在模块加载时注册到RouterModule
3. 路由名一致性:跳转时使用的路由名必须与注册时的路由名一致
4. URL格式:必须严格按照 `{har包名}/{路径}/{路由名}-{Builder名}` 的格式
5. 环境差异:由于环境差异,不建议直接编译,应参考示例代码进行编写
八 示例代码结构
entry (HAP包)
├── pages/Index.ets # 主页面,创建NavPathStack
├── RouterModule # 路由管理模块
│ └── utils/RouterModule.ets
harA (HAR包)
├── Index.ets # harInit函数
└── pages/
├── page1.ets # 注册Builder
├── page2.ets
└── page3.ets
harB (HAR包)
├── Index.ets # harInit函数
└── pages/
├── page1.ets # 注册Builder
├── page2.ets
└── page3.ets
九、面试回答模板(30秒版本)
> "Navigation动态路由实现了跨模块页面跳转。核心是RouterModule统一管理路由栈和页面构建器。使用步骤:1)主页面创建NavPathStack并注册;2)HAR包页面创建Builder并注册;3)通过URL格式调用push方法跳转。关键技术是动态import加载HAR包,wrapBuilder包装页面构建器,实现运行时动态调用。主要用于插件化架构和模块解耦场景。"
十、面试回答模板(2分钟详细版)
> "Navigation动态路由解决了跨模块页面跳转的问题,即使HAP包不直接依赖HAR包也能跳转。
> **实现分为4步:**
> 1. 创建RouterModule,用Map存储路由栈和Builder
> 2. 主页面创建NavPathStack并注册,Navigation组件绑定动态路由映射
> 3. HAR包页面创建Builder并用wrapBuilder包装后注册,实现harInit函数用于动态导入
> 4. 通过URL格式调用RouterModule.push跳转
> **核心机制:**
> - 动态import加载HAR包
> - wrapBuilder包装Builder支持运行时调用
> - URL解析获取路由信息
> **应用场景:** 插件化架构、模块解耦、按需加载。需要注意动态import的限制,需要用switch替代变量表达式。"
更多推荐



所有评论(0)