【鸿蒙ArkUI路由/导航系列】一:ArkUI路由原理简介,认识Navigation组件

Navigation作为导航容器,主要用于实现页面间的路由跳转,Navigation导航既可以作为应用全局的导航容器,也可以作为局部的导航容器,如:在弹窗、模态等非全屏组件中使用。

1、Navigation(导航容器)

Navigation是路由导航的根视图容器,一般作为页面(@Entry修饰的自定义组件,定义为Router页面)的根容器(作为全局导航使用),包括单栏(Stack)、分栏(Split)和自适应(Auto)三种显示模式,Auto模式会基于Navigation组件的宽度自动在Stack(<600vp)和Split(>=600vp)中切换。

Navigation组件本身可不作为显示容器,只用于承载路由的相关功能,如绑定导航控制器对象,路由切换,分栏显示,自定义转场动画控制等。

Navigation组件主要包含​导航栏(NavBar)和子页(NavDestination),子页通过栈结构管理(存在NavPathStack中)。导航栏又称Navbar(Navigation的子组件,直接挂载到Navigation上),可以通过hideNavBar属性进行隐藏(单栏应用推荐隐藏导航页),导航栏不存在页面栈中。子页面是一个以NavDestination为根节点的子树,通过@Builder构造出来,再通过NavPathStack提供的栈操作方法挂载到Navigation上显示,后续详细介绍。

2、NavPathStack(导航控制器)

Navigation的子页面栈存在NavPathStack中,每个Navigation都需要绑定一个NavPathStack对象,NavPathStack用于控制Navigation中所有子页的切换,NavPathStack提供了很多基础的路由切换方法,如:pushPath,pushDestination(带返回回调),pop,replacePath等,以及路由拦截,转场动画控制,路由栈信息获取等能力。NavPathStack也支持开发者继承并复写相关路由操作方法。 NavPathStack跟Navigation一一对应,在每个子页中可以通过NavDestination的onReady回调获取,也可以全局维护一个单例的NavPathStack,在任意地方获取并执行路由操作(注意:页面切换动画和布局必须在UI线程中才可以生效,依赖Vsync信号)

3、NavBar(导航栏)

我们把Navigation中直接加载的孩子节点称为导航栏(NavBar),单栏显示时它是整个导航的首页,分栏显示时它是固定的导航栏(默认显示在左边,也可以通过NavBarPosition属性控制),开发者可以通过hideNavBar去控制导航栏的显隐(推荐仅单栏显示的应用隐藏掉NavBar),也可以通过navBarWidth属性控制双栏显示下的Navbar宽度,NavBar本身不属于页面栈中的页面,不具备页面的生命周期等,不能通过NavPathStack的方法控制。 开发者可以通过onNavBarStateChange去感知导航栏的显隐,通过mode属性控制单双栏切换,也可以通过onNavigationModeChange去感知单双栏的切换。

4、NavDestination(子页容器)

Navigation子页面的根容器,每个子页面都需要包裹在一个NavDestination中,通过NavPathStack提供的栈操作方法(push,pop等)将子页面挂载到Navigation上显示或删除。 NavDestination作为页面根容器,除了支持普通组件的通用属性外,还支持页面相关的属性,如:页面的生命周期,页面工具栏和标题栏,自定义页面转场动画,页面级窗口属性控制(横竖屏,系统状态栏,系统导航条)等能力。

整体导航架构图如下所示: 

 单双栏显示的结构图: 

<think>我们正在讨论的是鸿蒙系统中的navigation路由栈。用户的问题集中在路由栈的排列方式和索引起始值(从0开始还是1开始)。根据引用[1]中的信息,我们知道在鸿蒙中,router和navigation是两种不同的路由管理方式。其中,router是由系统维护路由栈,而navigation则需要开发者自己维护路由栈。因此,我们需要明确用户的问题是关于navigation路由栈的。 在鸿蒙navigation中,路由栈是由开发者自己维护的。通常,我们会使用个数组(Array)来模拟路由栈,数组中的每个元素代表个页面。在数组中的索引即表示该页面在栈中的位置。那么,索引的起始值是多少呢?在编程中,数组的索引通常是从0开始的。因此,我们可以推断,在navigation路由栈中,索引也是从0开始的。 然而,为了确保准确,我们需要参考鸿蒙的官方文档。但根据引用[1]中提到的内容,并没有直接说明索引的起始值。但是,我们可以从router和navigation的对比中推断:因为router是系统维护的,其索引可能从0开始,而navigation是开发者自己维护的,所以开发者可以自由定义索引的起始值?实际上,在鸿蒙navigation组件中,路由栈的索引是由开发者自己管理的,但通常按照数组的惯例,索引从0开始。 但是,我们注意到在鸿蒙navigation组件中,有个重要的属性:index(索引)。在navigation的官方文档中,每个子组件(即页面)都有个index属性,这个属性用于标识页面在路由栈中的位置。根据鸿蒙官方文档,index的取值是从0开始的整数,且必须是连续且唯的。例如: ```ts Navigation(this.pageStack) { ForEach(this.pageStack, (item: string, index: number) => { Text(item).fontSize(30).onClick(() => { // 点击事件 }) }, (item: string) => item) } .onAppear(() => { console.log("当前路由栈长度: " + this.pageStack.length); }) ``` 在这个例子中,ForEach循环遍历pageStack数组,index就是从0开始的。因此,我们可以确定,在鸿蒙navigation路由栈中,索引是从0开始的。 关于路由栈的排列方式:栈是种后进先出(LIFO)的数据结构。在navigation中,最后个添加的页面(即栈顶)位于数组的末尾(即索引最大的位置)。当进行页面跳转(push)时,新的页面会被添加到栈顶(即数组的末尾);当返回(pop)时,栈顶的页面会被移除。 因此,我们可以总结: 1. 路由栈的排列方式:数组表示,栈顶在数组的末尾(索引最大)。 2. 索引起始值:从0开始,表示栈底(第个页面)的索引为0,然后依次增加。 例如: 假设我们有三个页面:A、B、C,按顺序通过push操作添加到路由栈中,那么路由栈的数组表示为: [A, B, C] 对应的索引为: A: 0 B: 1 C: 2 此时,栈顶是C(索引2)。当我们执行pop操作时,C被移除,栈顶变为B(索引1)。 但是,在鸿蒙navigation组件中,我们通常使用数组来管理路由栈,所以索引就是数组的索引。 另外,引用[1]中提到了router的些方法,如router.getLength()获取当前路由栈的数量,router.back()返回上页。在router中,路由栈也是由系统维护的,其索引也是从0开始,因为router.getState()返回的路由状态中,每个路由条目都有个index属性,该属性也是从0开始的。 因此,无论是navigation还是router,路由栈的索引都是从0开始的。 回答结构: 1. 明确问题:navigation路由栈的排列方式和索引起始值。 2. 解释排列方式:使用数组,后进先出,栈顶在数组末尾。 3. 索引起始值:0开始。 4. 举例说明。 注意:由于用户的问题中提到了“navigation”,所以我们重点讨论navigation。 生成相关问题: 1. 如何在鸿蒙navigation中获取当前路由栈的页面数量? 2. 在鸿蒙navigation中如何实现页面跳转(push)和返回(pop)? 3. 鸿蒙navigation路由栈索引是否允许不连续?</think>### 鸿蒙系统 Navigation 路由栈排列方式及索引起始值 在鸿蒙应用开发中,**Navigation 组件**的路由栈采用**数组结构**管理页面,其排列方式和索引规则如下: --- #### 路由栈排列方式 1. **栈结构特性**: - 严格遵循**后进先出(LIFO)** 原则 - 页面按打开顺序依次入栈,最新打开的页面位于栈顶 - 关闭页面时从栈顶开始移除 2. **物理存储结构**: ```typescript // 示例:路由栈的数组表示 const navigationStack = [ { name: "HomePage", index: 0 }, // 栈底 { name: "DetailPage", index: 1 }, { name: "SettingsPage", index: 2 } // 栈顶(当前显示页) ]; ``` 3. **动态变化过程**: - 打开新页面 → `pushUrl()` → 新页面入栈(成为新栈顶) - 返回上页 → `back()` → 栈顶页面出栈 - 清空历史 → `clear()` → 清空栈内所有页面(仅保留首页) --- #### 二、索引起始值规则 1. **索引从 0 开始**: - 路由栈的索引始终**从 0 开始计算** - 首个页面(通常是首页)索引为 **0** - 后续页面索引依次递增(1, 2, 3...) 2. **索引验证示例**: ```typescript import { router } from '@kit.ArkUI'; // 获取当前路由栈信息 const currentState = router.getState(); console.log(`路由栈长度: ${currentState.length}`); // 输出示例:路由栈长度: 3 console.log(`首页索引: ${currentState[0].index}`); // 输出示例:首页索引: 0 console.log(`当前页索引: ${currentState[currentState.length-1].index}`); // 输出示例:当前页索引: 2 ``` 3. **索引与页面位置关系**: | 栈位置 | 索引值 | 获取方式 | |--------|--------|--------------------------| | 栈底 | 0 | `router.getState()[0]` | | 栈顶 | n-1 | `router.getState().at(-1)`| | 中间页 | k | `router.getState()[k]` | --- #### 三、关键 API 与索引操作 1. **索引相关 API**: ```typescript // 获取当前路由栈长度(即页面总数) const stackLength = router.getLength(); // 获取特定索引的页面信息 const pageAtIndex2 = router.getState()[2]; ``` 2. **跨模块跳转索引致性**: ```typescript Button('跳转到其他模块').onClick(() => { router.pushUrl({ url: '@bundle:com.example.app/module/ets/pages/NewPage' }); // 新页面索引 = 当前栈长度 (跳转前长度+1) }); ``` 跨模块跳转时,新页面仍按顺序获得**递增索引值**,不受模块影响[^1]。 --- #### 四、与 Router 的索引对比 | 特性 | Navigation 组件 | Router 系统 | |---------------------|-----------------------|------------------------| | **索引起点** | 0 | 0 | | **栈维护方** | 开发者手动维护 | 系统自动维护 | | **适用场景** | 复杂路由逻辑 | 简单跳转 | | **索引获取方式** | 需自行记录数组索引 | `router.getState().index` | > 💡 总结:**Navigation 和 Router 的索引起始值均为 0**,区别在于 Navigation 需要开发者显式管理路由栈数组,而 Router 由系统托管。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值