前面讲了 Router 页面跳转和 Preferences 本地存储,列表跳详情、收藏存本地这套链路跑通了。
Router 在小项目里够用。但页面一多,比如底部有三个 Tab,每个 Tab 里还有子页面来回切,Router 的路径栈管理就开始吃力——哪个页面在栈顶、返回时回到哪一层,代码多了容易绕晕。
Navigation 是鸿蒙后来推的组件级导航方案,专门解决复杂页面结构下的路由问题。不管你是跟着教程自学,还是在重庆找鸿蒙培训系统学习,Navigation 都是必过的门槛——实际项目里多 Tab 应用是主流,Router 处理不了的场景,Navigation 能搞定。本篇把它的路径栈管理、Tab 嵌套导航、跟 Router 的适用场景一次讲清楚。
15.1 为什么 Router 不够用了
看这个场景:底部导航栏,三个 Tab——首页、发现、我的。从首页点进文章详情,再点进作者主页,然后切到“发现”Tab 看了一眼,再切回“首页”时,刚才打开的文章详情和作者主页都还在吗?
用 Router 管理的话,pushUrl 一路往下推,返回时用 back 一个一个退。但如果用户在几个 Tab 之间来回切,每个 Tab 的页面栈怎么保存、怎么恢复,Router 本身不管这些。需要自己在代码里用数组或 Map 存每个 Tab 的路径栈,切换时手动恢复——写起来很繁琐。
Navigation 的解决思路:给每个 Tab 分配一个独立的子导航栈,切 Tab 时各自的页面状态自动保留。
15.2 Navigation 路由原理
Navigation 是鸿蒙最核心的路由组件,既可以作为应用全局的导航容器,也可以在弹窗、模态等局部场景中使用。
Navigation 的组成结构
Navigation 由三部分构成:导航栏(NavBar)、子页(NavDestination)和页面栈管理器(NavPathStack)。
- NavBar:Navigation 自带的导航栏,可以隐藏,也可以用自定义内容替代。
- NavDestination:每个子页面的根容器,所有需要挂载到 Navigation 上的页面都要包裹在 NavDestination 中。它除了支持通用组件属性外,还支持页面生命周期、标题栏、工具栏、自定义转场动画、横竖屏控制等能力。
- NavPathStack:管理子页面切换的核心对象,每个 Navigation 都需要绑定一个 NavPathStack。它提供 pushPath、pushDestination(带返回回调)、pop、replacePath、clear 等方法,以及路由拦截、转场动画控制、路由栈信息获取等能力。
三种显示模式
Navigation 支持三种显示模式,能自动根据屏幕尺寸切换布局:
- Stack(单栏):宽度 < 600vp 时使用,全屏显示单个页面。
- Split(分栏):宽度 >= 600vp 时使用,左侧导航栏 + 右侧内容区。
- Auto(自适应):自动在 Stack 和 Split 之间切换,一套代码适配手机和平板。
页面栈管理
Navigation 的子页面栈存在 NavPathStack 中,NavPathStack 跟 Navigation 一一对应。在每个子页中可以通过 NavDestination 的 onReady 回调获取 NavPathStack,也可以全局维护一个单例,在任意地方获取并执行路由操作。
15.3 Navigation 基础跳转:从搭建到跑通
第一步:创建 NavPathStack 并绑定 Navigation
@Component
struct BasicNavigation {
stack: NavPathStack = new NavPathStack()
build() {
Navigation(this.stack) {
// Navigation 作为路由根容器,可以不显示任何内容
}
}
}
第二步:定义子页面
每个子页面需要包裹在 NavDestination 中,并声明对外的实例化方法:
// BasicNavDest.ets
@Builder
export function PageOneBuilder() {
BasicNavDestination({ name: 'pageOne' });
}
@Component
struct BasicNavDestination {
name: string = 'NA'
build() {
NavDestination() {
// 页面内容
}
}
}
第三步:配置路由表
在 entry/src/main/resources/base/profile/目录下创建 route_map.json
{
"routerMap": [
{
"name": "basicPageOne",
"pageSourceFile": "src/main/ets/pages/BasicFeature/BasicNavDest.ets",
"buildFunction": "PageOneBuilder"
}
]
}
在 module.json5 中注册路由表:
{
"module": {
"routerMap": "$profile:route_map"
}
}
第四步:跳转页面
this.stack.pushPath({ name: 'basicPageOne' });
返回上一页:
this.stack.popPath();
对于没有分栏需求的应用,建议将 Navigation 的导航栏隐藏,基于 NavDestination 组件进行所有页面功能的开发:
Navigation(this.stack) {
// ...
}
.hideNavBar(true)
15.4 NavDestination 的标题栏和工具栏
NavDestination 支持设置页面标题和底部工具栏。
NavDestination() {
// 页面内容
}
.title('单标题演示')
.title({
main: '主标题演示',
sub: '副标题演示'
})
.toolbarConfiguration([
{ value: "A" },
{ value: "B" },
{ value: "C" },
{ value: "D" },
{ value: "E" },
])
标题栏位于内容区上部,工具栏位于内容区下部。重复设置同一属性,后设置的会覆盖前者。
15.5 Tab 页里怎么嵌套子导航
三个 Tab,各自有独立的页面栈。Navigation 用 NavPathStack 的分组能力实现。
@Entry
@Component
struct TabNavigationDemo {
private homeStack: NavPathStack = new NavPathStack();
private discoverStack: NavPathStack = new NavPathStack();
private profileStack: NavPathStack = new NavPathStack();
@State currentTabIndex: number = 0;
build() {
Column() {
if (this.currentTabIndex === 0) {
Navigation(this.homeStack) {
HomeContent({ navStack: this.homeStack })
}
} else if (this.currentTabIndex === 1) {
Navigation(this.discoverStack) {
DiscoverContent({ navStack: this.discoverStack })
}
} else {
Navigation(this.profileStack) {
ProfileContent({ navStack: this.profileStack })
}
}
Row() {
Button('首页').onClick(() => { this.currentTabIndex = 0; })
Button('发现').onClick(() => { this.currentTabIndex = 1; })
Button('我的').onClick(() => { this.currentTabIndex = 2; })
}
.width('100%')
.height(56)
.justifyContent(FlexAlign.SpaceAround)
}
}
}
每个 Tab 对应一个独立的 NavPathStack。切到“发现”Tab 时,之前“首页”Tab 里打开的页面都保留在 homeStack 里,切回来直接恢复。
15.6 Navigation 和 Router,使用场景
定位
Navigation:组件级导航容器,可作全局或局部导航。
Router:页面级全局路由,页面需在 main_pages.json 中注册。
显示模式
Navigation:单栏、分栏、自适应三种,能根据屏幕尺寸自动切换。
Router:仅全屏跳转。
路由操作
Navigation:pushPath、pushDestination(带返回回调)、pop、replacePath、clear 等,支持栈的精细操作。
Router:pushUrl、back、replaceUrl,栈操作相对简单。
参数传递
Navigation:NavPathInfo 对象传参,通过 onReady 或 getParamByIndex 获取,支持返回传参。
Router:params 传参,通过 getParams 获取。
转场动画
Navigation:自定义动效、共享元素转场。
Router:基本动画,自定义能力有限。
生命周期
Navigation:NavDestination 组件事件管理,支持路由拦截。
Router:UIObserver.on('routerPageUpdate') 监听,拦截能力弱。
适用场景
Navigation:中大型应用、多端适配、有底部 Tab 或侧边栏的结构。
Router:简单线性页面流,开发门槛低。
新项目、有底部 Tab、页面结构复杂,用 Navigation。单线跳转的小项目 Router 依然能用,但官方重心在 Navigation 上。
15.7 实战应用:用 Navigation 改造文章列表跳转详情页
把上一篇的列表-详情-收藏功能,从 Router 改成 Navigation。
主页面:Navigation 包裹,底部两个 Tab
@Entry
@Component
struct MainNavigationPage {
private articleStack: NavPathStack = new NavPathStack();
private favoriteStack: NavPathStack = new NavPathStack();
@State currentTab: number = 0;
build() {
Column() {
if (this.currentTab === 0) {
Navigation(this.articleStack) {
ArticleListContent({ navStack: this.articleStack })
}
} else {
Navigation(this.favoriteStack) {
FavoriteListContent({ navStack: this.favoriteStack })
}
}
Row() {
Button('文章').onClick(() => { this.currentTab = 0; })
Button('收藏').onClick(() => { this.currentTab = 1; })
}
.width('100%')
.height(56)
.justifyContent(FlexAlign.SpaceAround)
}
}
}
文章列表子组件:接收 navStack,点文章跳详情
@Component
struct ArticleListContent {
@Prop navStack: NavPathStack;
build() {
List() {
ListItem() {
Text('文章标题')
.onClick(() => {
this.navStack.pushPath({ name: 'DetailPage', param: { articleId: '001' } });
})
}
}
}
}
15.8 本篇总结
- Navigation 是鸿蒙主推方案:组件级导航容器,支持分栈管理、三种显示模式、自定义动效。
- 核心结构:NavBar + NavDestination + NavPathStack,三者配合完成页面管理。
- 基础跳转:创建 NavPathStack → 绑定 Navigation → 配置路由表 → pushPath 跳转。
- Tab 导航:每个 Tab 分配独立 NavPathStack,切换时状态自动保留。
常见问题
Q: Navigation 是鸿蒙一开始就有的吗?
A: 不是。Navigation 是后续版本推出的,用来解决 Router 在多 Tab、复杂导航场景下的不足。现在官方主推 Navigation。
Q: Navigation 和 Router 怎么选?
A: 新项目、有底部 Tab、页面结构复杂、需要多端适配用 Navigation。简单线性跳转的小项目 Router 依然能用。
Q: 用 Navigation 之后,Router 代码要全改吗?
A: 跳转写法从 pushUrl/back 换成 pushPath/popPath,传参方式类似。逻辑不用大改,主要是把 Navigation 容器搭起来。
Q: Tab 切换时怎么保留每个 Tab 的页面状态?
A: 每个 Tab 分配独立的 NavPathStack,切换时栈不销毁,切回来页面状态还在。
学完 Navigation,页面跳转和本地存储两条线都实现了。列表到详情、收藏持久化、Tab 分栈导航——这些组合起来,一个 App 的骨架基本成型。文章列表现在还是模拟数据,接下来解决数据获取。下一篇讲网络请求,用 http 模块从服务器拉真实数据,async/await 处理异步,让 App 真正“活”起来。
如果你在自学过程中遇到问题,或者想找个重庆本地的鸿蒙培训系统学一遍,重庆华鸿科技正在做鸿蒙技术培训,跟赛力斯、阿维塔、重钢集团有深度合作,培训内容对接企业实际用人需求。会用真实项目把组件、路由、存储、网络请求这些零散知识点串起来——模块之间的衔接、数据的流转、问题的 debug,比跟着教程学印象深得多。