一、背景
在鸿蒙开发中,提供了两种路由导航方式,分别是Router和Navigation,之前的文章介绍过Router的使用,此篇文章主要总结下Navigation的使用及与Router对比区别
二、Navigation简介
2.1、简介
Navigation是路由导航的根视图容器,一般作为页面(@Entry)的根容器,包括单栏(Stack)、分栏(Split)和自适应(Auto)三种显示模式。Navigation组件适用于模块内和跨模块的路由切换,通过组件级路由能力实现更加自然流畅的转场体验,并提供多种标题栏样式来呈现更好的标题和内容联动效果。一次开发,多端部署场景下,Navigation组件能够自动适配窗口显示大小,在窗口较大的场景下自动切换分栏展示效果。
2.2、关键属性与方法
| 属性 | 类型 | 描述 |
|---|---|---|
title |
ResourceStr | CustomBuilder |
页面标题。支持字符串、资源引用或自定义构建器。 |
titleMode |
NavigationTitleMode |
标题栏显示模式(如 Full,Mini,Free)。 |
hideTitleBar |
boolean |
是否隐藏标题栏。 |
hideToolBar |
boolean |
是否隐藏工具栏。 |
menus |
Array<NavigationMenuItem> | CustomBuilder |
设置页面右上角菜单。 |
toolbarConfiguration |
Array<ToolbarItem> | CustomBuilder |
设置工具栏内容(替代已废弃的 toolBar)。 |
hideBackButton |
boolean |
是否隐藏返回键(对 NavDestination 的返回图标无效)。 |
mode |
NavigationMode |
导航栏显示模式,如支持单栏(Stack)、分栏(Split)和自适应(Auto) 默认值:NavigationMode.Auto 自适应:基于组件宽度自适应单栏和双栏。 |
navDestination |
(name: string, param: unknown) => void |
重要:用于创建 NavDestination 组件的构建器函数。 |
三、Navigation基本用法
有两种使用方法,分别为动态构建和静态配置
3.1、动态构建
TypeScript
@Entry
@Component
struct Index {
@Provide('pageInfos') pageInfos: NavPathStack = new NavPathStack()
aboutToAppear() {
// 压入初始页面
this.pageInfos.pushPathByName("home", "");
}
@Builder
routerMap(name: string) {
if (name === "home") {
Home()
} else if (name === "mine") {
Mine();
}
}
build() {
Navigation(this.pageInfos) {
}
.hideNavBar(true)
.navDestination(this.routerMap)
}
}
@Component
struct Home {
// 使用@Consume装饰器获取页面栈
@Consume('pageInfos') pageInfos: NavPathStack;
build() {
NavDestination() {
Button('Home页面')
.onClick(()=>{
this.pageInfos.pushPathByName('mine','')
})
}.title('Home')
}
}
@Component
struct Mine {
// 使用@Consume装饰器获取页面栈
@Consume('pageInfos') pageInfos: NavPathStack;
build() {
NavDestination() {
Text('Mine页面')
}.title('Mine')
.onBackPressed(() => {
const popDestinationInfo = this.pageInfos.pop(); // 弹出路由栈栈顶元素
console.info('pop' + '返回值' + JSON.stringify(popDestinationInfo));
return true;
})
}
}
3.2、静态配置
路由导航主要元素:
1、导航页Navigation和子页NavDestination
2、路由表RouterMap
3、路由栈NavPathStack
3.2.1、构建导航栏
TypeScript
@Entry
@Component
struct Index {
pageStack : NavPathStack = new NavPathStack();
build() {
Navigation(this.pageStack){
}.onAppear(() => {
this.pageStack.pushPathByName("PageOne", null, false); //添加路由操作
})
.hideNavBar(true)
}
}
3.2.2、构建子页
TypeScript
// 跳转页面入口函数
@Builder
export function PageOneBuilder() {
PageOne();
}
@Component
struct PageOne {
pathStack: NavPathStack = new NavPathStack();
build() {
NavDestination() {
Button('跳转')
}
.title('PageOne')
.onReady((context: NavDestinationContext) => {
this.pathStack = context.pathStack;
})
}
}
3.2.3、创建路由表
1、在跳转目标模块的配置文件module.json5添加路由表配置:
TypeScript
{
"module" : {
"routerMap": "$profile:route_map" //添加路由表配置数据
}
}
2、添加完路由配置文件地址后,需要在工程resources/base/profile中创建route_map.json文件。添加如下配置信息:
TypeScript
{
"routerMap": [
{
"name": "PageOne",
"pageSourceFile": "src/main/ets/view/PageOne.ets",
"buildFunction": "PageOneBuilder",
"data": {
"description": "this is PageOne"
}
},
{
"name": "PageTwo",
"pageSourceFile": "src/main/ets/view/PageTwo.ets",
"buildFunction": "PageTwoBuilder",
"data": {
"description": "this is PageTwo"
}
}
]
}
配置说明如下:
| 配置项 | 说明 |
|---|---|
| name | 可自定义的跳转页面名称。 |
| pageSourceFile | 跳转目标页在包内的路径,相对src目录的相对路径。 |
| buildFunction | 跳转目标页的入口函数名称,必须以@Builder修饰。 |
| data | 应用自定义字段。可以通过配置项读取接口getConfigInRouteMap获取。 |
3.2.4、路由操作
TypeScript
@Entry
@Component
struct Index {
pageStack : NavPathStack = new NavPathStack();
build() {
Navigation(this.pageStack){
}.onAppear(() => {
this.pageStack.pushPathByName("PageOne", null, false); //添加路由操作
})
.hideNavBar(true)
}
}
TypeScript
// 跳转页面入口函数
@Builder
export function PageOneBuilder() {
PageOne();
}
@Component
struct PageOne {
pathStack: NavPathStack = new NavPathStack();
build() {
NavDestination() {
Button('跳转')
.onClick(()=>{
this.pathStack.pushPathByName('PageTwo','') //路由操作
})
}
.title('PageOne')
.onReady((context: NavDestinationContext) => {
this.pathStack = context.pathStack;
})
}
}
3.3、两种方法主要区别对比
| 特性 | 动态构建 | 静态配置 |
|---|---|---|
| 配置方式 | 代码内@Builder函数 |
外部JSON配置文件 |
| 类型检查 | 运行时检查 | 编译时强类型检查 |
| 热更新 | 支持 | 不支持,需要重新打包 |
| 维护性 | 代码路由耦合 | 配置与代码分离 |
| 适用场景 | 简单应用、快速原型 | 大型应用、团队协作 |
四、Navigation生命周期
Navigation作为路由容器,其生命周期承载在NavDestination组件上,以组件事件的形式开放。

- 根页面(@Entry修饰的页面)生命周期
- 遵从标准页面生命周期,关键事件包括:
aboutToAppear:页面即将显示时触发。onPageShow:页面显示后触发(注意:不推荐在Navigation根页面使用)。aboutToDisappear:页面即将隐藏时触发。
- NavDestination子页面生命周期
- 通过组件事件监听:
onAppear:通用生命周期事件,子页面显示时触发。onShown:子页面布局完全显示后触发。onActive:子页面处于激活态时触发(如从后台回到前台)。onInactive:子页面失去激活态时触发(如应用退到后台)。
| 生命周期事件 | 触发时机说明 |
|---|---|
aboutToAppear |
自定义组件生命周期 :在创建自定义组件后,执行其 build() 函数之前调用。 |
onWillAppear |
NavDestination 独有 :NavDestination 创建后,挂载到组件树之前执行。 |
onAppear |
通用组件生命周期 :NavDestination 组件挂载到组件树时执行。 |
onWillShow |
NavDestination 独有 :组件布局显示之前执行,此时页面不可见。 |
onShown |
NavDestination 独有:组件布局显示之后执行,此时页面已完成布局。 |
onActive |
NavDestination 独有:页面处于激活态(处于栈顶且可操作)时触发。 |
onWillHide |
NavDestination 独有:组件触发隐藏之前执行(应用切换到后台不会触发)。 |
onInactive |
NavDestination 独有:页面变为非激活态(如被遮挡或非栈顶)时触发。 |
onHidden |
NavDestination 独有:组件触发隐藏后执行(如新页面入栈、当前页出栈或应用切后台)。 |
onWillDisappear |
NavDestination 独有:组件即将销毁之前执行,有转场动画则在动画前触发。 |
onDisappear |
通用组件生命周期 :NavDestination 组件从组件树上卸载时执行。 |
aboutToDisappear |
自定义组件生命周期:自定义组件析构销毁之前执行。 |
五、Navigation 与 Router 的对比
| 业务场景 / 能力 | Navigation | Router |
|---|---|---|
| 官方推荐与演进 | 是未来主推方向,持续演进新功能 | 后续不再新增功能 |
| 一多能力 | 支持,自适应不同设备与屏幕 | 不支持 |
| 路由栈管理 | 支持 ,可获取并操作 NavPathStack |
不支持,只能获取基本状态信息 |
| 参数传递方式 | 引用传递,性能更优 | 深拷贝,性能开销相对较大 |
| 路由数量限制 | 无限制 | 最多32个页面 |
| 传参获取 | 支持获取指定页面的参数 | 不支持获取指定页面参数 |
| 沉浸式页面 | 原生支持 | 需通过 Window 配置实现 |
| 模态嵌套路由 | 支持在模态框、弹窗中嵌套 | 不支持 |
| 共享元素动画 | 支持 | 不支持 |
| 路由拦截 | 支持 | 不支持 |
| 页面结构 | 页面是组件,由 NavDestination 包裹 |
页面是 @Entry 修饰的独立组件 |
| 适用场景 | 同一模块内的页面跳转,复杂UI结构 | 跨模块的页面跳转 |
使用建议:
-
优先使用 Navigation:特别是新启动的项目,或者在同一个模块内进行页面跳转时。它提供了更强大的功能和更好的性能。
-
Router 的适用场景 :当需要进行跨模块的页面跳转时(例如主模块跳转到动态共享包中的页面),目前仍需使用 Router。