写鸿蒙开发也有段时间了,这阵子帮团队把一个主力业务从旧的框架迁到鸿蒙原生,页面路由和组件跳转这块,是所有人问得最多、踩坑也最密集的环节。网上教程不少,但大多停留在“能跳能返”的层面,等真到了带参数、带回调、跨页面刷新、组件间通信的时候,资料一下就稀碎了。所以想把这段时间的实战经验整理出来,给准备做鸿蒙APP开发的朋友当个参考。
这篇文章不是照着API文档念,我会用一个商品列表到详情页再到购物车的完整场景,把页面路由的底层逻辑、Router和Navigation两套方案怎么选、参数怎么传、页面间数据怎么回流、组件间怎么通信这些事一次讲透。适合两类人看:一是刚开始接触鸿蒙开发、被路由和页面跳转绕晕的新手,二是已经能写页面但想在架构层面把路由和通信设计得更规范的同学。
1. 先搞明白鸿蒙里的“页面”到底是个什么玩意
1.1 页面、UIAbility和Stage模型的关系
很多从Android或者iOS转过来的开发者,第一步就卡在概念上。鸿蒙里没有Activity,也没有ViewController,而是引入了“UIAbility + 页面”的层级结构。UIAbility对应的是系统级的能力单元,可以粗略理解为一个“进程身份的载体”,它本身不负责画界面,真正展示给用户的是它内部的页面。一个UIAbility可以承载多个页面,页面之间通过路由跳转,就像把一个Activity内部塞了多个Fragment来回切换。
这里有个关键点:页面在鸿蒙里是用ArkUI框架渲染的,根节点是@Entry修饰的自定义组件。也就是说,一个页面本质上就是一个组件,只不过它被标记成了“入口组件”。这个设计直接影响你对路由的理解——路由跳转不只是换一个界面那么简单,它还牵涉到组件树的创建、销毁、状态保留和恢复。
我在项目里经常跟团队说一句话:“鸿蒙页面的生命周期,就是组件树的生命周期。”你把页面理解成一个普通组件,很多事情就豁然开朗了。比如为什么返回上一页的时候前一个页面的状态还在?因为系统只是把它的组件树从路由栈里恢复了出来,并没有重新创建整个实例。
1.2 页面栈的运作机制
鸿蒙的页面路由是基于“页面栈”实现的,这个概念跟Android的返回栈几乎一样。每次通过路由跳转压入一个新页面,栈顶就是当前可见页面;点击返回或者调用返回接口,栈顶页面弹出销毁,上一个页面重新变成可交互状态。
这里要注意的是,路由栈不是无限深的。虽然官方文档没有给出绝对上限,但压入太多页面会占用大量内存,尤其是在低端设备上,页面过多会导致卡顿甚至直接崩溃。我在实测中发现,一个商品详情页如果反复进入5到8次不返回,内存占用就会明显上涨,这时候要么主动清理中间层页面,要么改用replaceUrl替换当前页面。
页面栈管理的另一个坑是“单例页面”。比如你从首页进到商品详情,再从详情进到店铺,然后又从店铺进到另一个商品详情,这时候栈里会同时存在两个商品详情页。如果你的详情页依赖一个全局状态来渲染,那新页面会覆盖旧页面,但返回的时候旧页面又会重新出现,状态可能已经变了,就会出现“商品对不上”的诡异问题。解决思路很简单:要么在跳转前查询栈里有没有同名页面,要么用路由的初始化参数来校验是否复用已有实例。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 路由方案选型:Router和Navigation到底怎么选
2.1 两套方案的核心差异
鸿蒙现在实际上有两套路由方案:一套是早期的Router,基于@ohos.router模块,用router.pushUrl来跳转;另一套是后续主推的Navigation,基于NavPathStack组件栈,通过this.pathStack.pushPath来跳转。两者都能实现页面跳转和参数传递,但设计思路差别很大。
Router更像传统命令式路由:你给一个URL,系统去目标解析并跳转。优点是上手快,代码写在按钮点击里就行,适合页面数量少、层级简单的场景。缺点是路由信息相对分散,每个跳转点都在代码里可执行一次,页面全局状态管理弱,不好做深链和动态路由。
Navigation则是声明式加命令式的混合方案:页面跳转的层级关系由NavPathStack统一维护,目标页面可以提前配置在路由表里,也可以运行时动态添加。它天然支持页面生命周期感知、跨页面的状态回传,而且在DevEco Studio 4.0之后的版本里,官方对Navigation的性能优化明显更积极。
2.2 为什么官方在推Navigation
我个人的感受是,Navigation不是“新玩具”,而是鸿蒙为应对复杂业务场景给出的标准答案。它的核心优势有三个:
第一,路由状态集中管理。 NavPathStack本身
