当前位置:首页>鸿蒙APP>鸿蒙 Navigation 怎么用?路径栈管理、Tab 页内嵌导航,跟 Router 有什么不同 | 从零到一第15篇

鸿蒙 Navigation 怎么用?路径栈管理、Tab 页内嵌导航,跟 Router 有什么不同 | 从零到一第15篇

  • 2026-10-22 21:44:58
鸿蒙 Navigation 怎么用?路径栈管理、Tab 页内嵌导航,跟 Router 有什么不同 | 从零到一第15篇

上一篇讲了 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

@Componentstruct BasicNavigation {  stack: NavPathStack = new NavPathStack()  build() {    Navigation(this.stack) {      // Navigation 作为路由根容器,可以不显示任何内容    }  }}

第二步:定义子页面

每个子页面需要包裹在 NavDestination 中,并声明对外的实例化方法:

// BasicNavDest.ets@Builderexport function PageOneBuilder() {  BasicNavDestination({ name: 'pageOne' });}@Componentstruct 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@Componentstruct 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
定位
组件级导航容器
页面级全局路由
显示模式
单栏/分栏/自适应
仅全屏跳转
路由操作
pushPath、pop、replacePath、clear 等
pushUrl、back、replaceUrl
参数传递
NavPathInfo 对象传参
params 传参
转场动画
自定义动效、共享元素
基本动画
生命周期
NavDestination 组件事件
UIObserver 监听
适用场景
中大型复杂应用、多端适配
简单线性页面流

新项目、有底部 Tab、页面结构复杂用 Navigation。单线跳转的小项目 Router 依然能用,但官方重心在 Navigation 上。

15.7 实战应用:用 Navigation 改造文章列表跳转详情页

把上一篇的列表-详情-收藏功能,从 Router 改成 Navigation。

主页面:Navigation 包裹,底部两个 Tab

@Entry@Componentstruct 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,点文章跳详情

@Componentstruct 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,比跟着教程学印象深得多。

《从零到一学鸿蒙原生开发》(点击跳转):

《从零到一:鸿蒙原生应用开发实战》第1篇:搞懂鸿蒙的“世界观”——一次开发多端部署、元服务、分布式到底是什么
《从零到一:鸿蒙原生应用开发实战》第2篇:工欲善其事——搭建鸿蒙开发环境(零失败指南)
《从零到一:鸿蒙原生应用开发实战》第3篇:Hello World ——逐行拆解项目结构,手写你的第一个 ArkTS 页面
《从零到一:鸿蒙原生应用开发实战》第4篇:ArkTS 基础语法速成(上)—— 数据类型、变量、函数、箭头函数
《从零到一:鸿蒙原生应用开发实战》第5篇:ArkTS 基础语法速成(下)—— 数组、对象、解构、扩展运算符
《从零到一:鸿蒙原生应用开发实战》第6篇:声明式UI入门——告别命令式旧思维,手写第一个ArkUI组件
《从零到一:鸿蒙原生应用开发实战》第7篇:@State 装饰器入门——状态变,界面自动更新
《从零到一:鸿蒙原生应用开发实战》第8篇:@Prop 和 @Link 详解——父子组件传值,需要搞懂这两个装饰器
《从零到一:鸿蒙原生应用开发实战》第9篇:跨层级共享——@Provide 与 @Consume 怎么绕过中间组件传数据
《从零到一:鸿蒙原生应用开发实战》第10篇:Text 与 Image两个最基础的组件——讲几个细节
《从零到一:鸿蒙原生应用开发实战》第11篇:Button 和 TextInput,做一个登录页面

《从零到一:鸿蒙原生应用开发实战》第12篇:Flex 弹性布局——ArkUI 有没有 CSS 一样的弹性布局?

《从零到一:鸿蒙原生应用开发实战》第13篇:List 组件详解——ForEach 循环渲染与 LazyForEach 懒加载

《从零到一:鸿蒙原生应用开发实战》第14篇:页面跳转和本地存储——列表页到详情页,收藏退出不丢失

本文由「华鸿技术栈」原创发布,专注鸿蒙(HarmonyOS)原生应用与元服务开发。从零到一分享实战代码与项目案例,对接鸿蒙技术培训资源与上市公司研发岗位,为开发者铺平入行之路。不讲虚的,只上干货。转载请联系授权。

最新文章

随机文章