鸿蒙 从router切换navigation
本文对比分析了HarmonyOS中Router和Navigation两种路由方式的实现差异。主要内容包括:1.页面结构方面,Router使用@Entry修饰组件,Navigation分为导航页和子页;2.路由操作方式不同,Router通过router模块方法操作,Navigation通过NavPathStack控制;3.生命周期管理机制差异;4.转场动画和共享元素转场支持情况;5.跨包路由实现方案
一、页面结构
1.1 Router页面结构
Router路由的页面是一个@Entry修饰的Component,每一个页面都需要在main_page.json中声明。
// main_page.json
{
"src": [
"pages/Index",
"pages/pageOne",
"pages/pageTwo"
]
}
// Router页面示例
@Entry
@Component
struct Index {
build() {
Button('router to pageOne')
.onClick(() => {
this.getUIContext().getRouter().pushUrl({
url: 'pages/pageOne'
});
})
}
}
1.2 Navigation页面结构
Navigation的路由页面分为导航页(Navbar)和子页(NavDestination):
-
导航页(Navbar):Navigation包含的子组件
-
子页(NavDestination):NavDestination包含的子组件
// Navigation导航页示例
@Entry
@Component
struct Index {
pathStack: NavPathStack = new NavPathStack();
build() {
Navigation(this.pathStack) {
Button('Push PageOne')
.onClick(() => {
this.pathStack.pushPathByName('navigation_pageOne', null);
})
}
.title('Navigation')
}
}
// Navigation子页示例
@Builder
export function PageOneBuilder() {
PageOne();
}
@Component
export struct PageOne {
pathStack: NavPathStack = new NavPathStack();
build() {
NavDestination() {
Button('回到首页')
.onClick(() => {
this.pathStack.clear();
})
}
.title('PageOne')
.onReady((context: NavDestinationContext) => {
this.pathStack = context.pathStack;
})
}
}
注意:每个子页面也需要配置到系统配置文件router_map.json中(参考系统路由表)。
二、路由操作
2.1 Router路由操作
Router通过@ohos.router模块提供的方法来操作页面,建议使用UIContext中的getRouter获取Router对象。
// push page
this.getUIContext().getRouter().pushUrl({ url: 'pages/pageOne', params: null });
// pop page
this.getUIContext().getRouter().back({ url: 'pages/pageOne' });
// replace page
this.getUIContext().getRouter().replaceUrl({ url: 'pages/pageOne' });
// clear all page
this.getUIContext().getRouter().clear();
// 获取页面栈大小
let size = this.getUIContext().getRouter().getLength();
// 获取页面状态
let pageState = this.getUIContext().getRouter().getState();
2.2 Navigation路由操作
Navigation通过导航控制器对象NavPathStack提供的方法来操作页面,需要创建一个栈对象并传入Navigation中。
@Entry
@Component
struct Index {
pathStack: NavPathStack = new NavPathStack();
build() {
Navigation(this.pathStack) {
// 导航内容
}
}
}
// push page
this.pathStack.pushPath({ name: 'pageOne' });
// pop page
this.pathStack.pop();
this.pathStack.popToIndex(1);
this.pathStack.popToName('pageOne');
// replace page
this.pathStack.replacePath({ name: 'pageOne' });
// clear all page
this.pathStack.clear();
// 获取路由栈大小
let size: number = this.pathStack.size();
// 删除栈中name为PageOne的所有页面
this.pathStack.removeByName('pageOne');
// 删除指定索引的页面
this.pathStack.removeByIndexes([1, 3, 5]);
// 获取栈中所有页面name集合
this.pathStack.getAllPathName();
// 获取索引为1的页面参数
this.pathStack.getParamByIndex(1);
// 获取PageOne页面的参数
this.pathStack.getParamByName('pageOne');
// 获取PageOne页面的索引集合
this.pathStack.getIndexByName('pageOne');
2.3 获取NavPathStack的几种方式
Navigation子页面想要做路由需要拿到Navigation持有的导航控制器对象NavPathStack:
| 方式 | 说明 | 推荐度 |
|---|---|---|
| 方式一:@Provide/@Consume | 有耦合,不推荐 | ⭐⭐ |
| 方式二:onReady回调 | 推荐 | ⭐⭐⭐⭐⭐ |
| 方式三:AppStorage全局设置 | 推荐 | ⭐⭐⭐⭐ |
| 方式四:queryNavigationInfo | 推荐 | ⭐⭐⭐⭐ |
方式二:通过OnReady回调获取(推荐)
@Component
export struct PageOne {
pathStack: NavPathStack = new NavPathStack();
build() {
NavDestination() {
// 页面内容
}
.onReady((context: NavDestinationContext) => {
this.pathStack = context.pathStack;
})
}
}
方式三:通过AppStorage全局设置
@Entry
@Component
struct Index {
pathStack: NavPathStack = new NavPathStack();
aboutToAppear(): void {
AppStorage.setOrCreate('PathStack', this.pathStack);
}
build() {
Navigation(this.pathStack) { }
}
}
// 子页面获取
pathStack: NavPathStack = AppStorage.get('PathStack') as NavPathStack;
方式四:通过queryNavigationInfo查询
@Component
struct CustomNode {
pathStack: NavPathStack = new NavPathStack();
aboutToAppear() {
let navigationInfo: NavigationInfo = this.queryNavigationInfo() as NavigationInfo;
if (navigationInfo != undefined) {
this.pathStack = navigationInfo.pathStack;
}
}
}
三、生命周期
3.1 Router页面生命周期
Router页面生命周期为@Entry页面中的通用方法:
// 页面创建后挂树的回调
aboutToAppear(): void { }
// 页面销毁前下树的回调
aboutToDisappear(): void { }
// 页面显示时的回调
onPageShow(): void { }
// 页面隐藏时的回调
onPageHide(): void { }
3.2 Navigation页面生命周期
Navigation作为路由容器,其生命周期承载在NavDestination组件上,以组件事件的形式开放:
@Entry
@Component
struct PageOne {
build() {
NavDestination() {
// 页面内容
}
.onWillAppear(() => { }) // 页面即将显示
.onAppear(() => { }) // 页面显示
.onWillShow(() => { }) // 页面即将展示
.onShown(() => { }) // 页面已展示
.onWillHide(() => { }) // 页面即将隐藏
.onHidden(() => { }) // 页面已隐藏
.onWillDisappear(() => { }) // 页面即将销毁
.onDisAppear(() => { }) // 页面已销毁
}
}
3.3 生命周期关系说明
| 规则 | 说明 |
|---|---|
| Router影响Navigation | Router页面的跳转会影响其内部Navigation页面的生命周期 |
| Navigation不影响Router | Navigation页面的跳转不会影响其所在router页面的生命周期 |
| 前后台共同触发 | 应用前后台切换会同时触发router页面和Navigation页面的生命周期 |
四、转场动画
| 对比项 | Router | Navigation |
|---|---|---|
| 转场动画 | 支持 | 支持 |
| 自定义转场 | pageTransition()通用方法 | customNavContentTransition事件 |
| Dialog类型页面 | - | API 13前无动画,13后支持系统转场 |
五、共享元素转场
| 对比项 | Router | Navigation |
|---|---|---|
| 共享元素转场 | sharedTransition通用属性 | geometryTransition属性(NavDestination之间切换) |
六、跨包路由
6.1 Router跨包路由
Router通过命名路由的方式实现跨包跳转。
// 共享包中定义
@Entry({ routeName: 'myPage' })
@Component
export struct MyComponent { }
// 跳转页面
import('library/src/main/ets/pages/Index');
this.getUIContext().getRouter().pushNamedRoute({
name: 'myPage',
params: { data1: 'message' }
});
6.2 Navigation跨包路由
Navigation作为路由组件,默认支持跨包跳转。
// 1. 导入跨包的路由页面
import { PageInHSP } from 'library';
@Entry
@Component
struct mainPage {
pageStack: NavPathStack = new NavPathStack();
@Builder pageMap(name: string) {
if (name === 'PageInHSP') {
PageInHSP(); // 2. 定义路由映射表
}
}
build() {
Navigation(this.pageStack) {
Button('Push HSP Page')
.onClick(() => {
this.pageStack.pushPath({ name: 'PageInHSP' }); // 3. 跳转
})
}
.navDestination(this.pageMap)
}
}
七、动态路由
7.1 动态路由的优势
| 优势 | 说明 |
|---|---|
| 配置丰富 | 可配置横竖屏默认模式、是否需要鉴权等扩展信息 |
| 按名称跳转 | 按照名称进行跳转而不是ets文件路径 |
| 按需加载 | 使用动态Import,防止首个页面加载大量代码 |
7.2 Navigation动态路由实现方案
| 方案 | 说明 | 起始版本 |
|---|---|---|
| 自定义路由表 | 开发自定义路由管理模块,通过WrappedBuilder封装 | - |
| 系统路由表 | 系统管理路由表,各模块独立配置router_map.json | API 12 |
八、生命周期监听
| 框架 | 监听方式 |
|---|---|
| Router | uiObserver.on('routerPageUpdate') |
| Navigation | UIObserver.on('navDestinationUpdate') |
// Navigation生命周期监听示例
let uiObserver: UIObserver = uiContext.getUIObserver();
uiObserver.on('navDestinationUpdate', (info) => {
// NavDestinationState.ON_SHOWN = 0, ON_HIDE = 1
if (info.state == 0) {
hilog.info(TAG, 'page ON_SHOWN:' + info.name.toString())
}
})
九、页面信息查询
| 框架 | 查询接口 | 返回值 |
|---|---|---|
| Router | queryRouterPageInfo() |
RouterPageInfo(包含context、index、name、path、state、pageId) |
| Navigation | queryNavDestinationInfo() |
NavDestinationInfo(包含navigationId、name、state、index、param、navDestinationId) |
十、路由拦截
| 框架 | 路由拦截能力 |
|---|---|
| Router | 不支持,需自行封装 |
| Navigation | 支持setInterception方法设置页面跳转拦截回调 |
核心对比
| 对比项 | Router | Navigation |
|---|---|---|
| 页面结构 | @Entry组件 | Navbar + NavDestination |
| 路由栈操作 | router对象方法 | NavPathStack方法 |
| 生命周期 | aboutToAppear等 | NavDestination事件 |
| 跨包路由 | 命名路由 | 默认支持 |
| 动态路由 | 自定义 | 自定义/系统路由表 |
| 路由拦截 | 不支持 | setInterception |
迁移推荐
| 场景 | 推荐方案 |
|---|---|
| 获取路由栈 | onReady回调 |
| 跨包路由 | 系统路由表 |
| 动态路由 | 系统路由表(API 12+) |
| 路由拦截 | setInterception |
更多推荐




所有评论(0)