做鸿蒙应用开发,绕不开页面路由和组件跳转,最典型的场景就是:用户点了一个列表项,跳进详情页,改完数据再退回来,列表得跟着刷新。这个需求看起来平平无奇,但真从零开始搭过一次路由体系,你会发现里面藏着一整套状态管理和生命周期的问题。我见过不少项目,业务代码写得还行,一到页面跳转就乱了——要么返回的时候数据不刷新,要么页面栈越堆越深,要么跨模块跳转直接白屏。
这篇内容就把页面路由与组件跳转这条线完整捋一遍,从路由方案选型、Navigation落地、参数传递,到生命周期和页面栈管理,把那些官方文档里没细说、但实际开发一定会踩的坑也一并讲清楚。适合刚开始接触鸿蒙开发的朋友,也适合已经在写业务、但想系统梳理路由机制的开发者。看完你至少能明白:页面应该怎么组织、参数应该怎么传、返回刷新应该怎么做、页面栈为什么会越堆越深。
1. 路由方案选型:别急着写代码,先想清楚Router和Navigation
我先说结论:新项目请直接上Navigation,老项目如果还在用Router,可以逐步迁移,但不建议再往Router里加新页面了。原因不是Router不能用,而是它的设计思路和鸿蒙当前推荐的工程化方向已经不太匹配。
1.1 Router和Navigation的定位差异
早期鸿蒙提供的是router模块,风格很接近Web前端的路由:router.pushUrl({ url: 'pages/Detail', params }) 就能跳转。对应的页面是Page级别的,系统管跳转历史,页面之间天然隔离。这个设计轻量、直接,API也简单,适合Demo和单模块的小项目。
Navigation是后面逐渐成熟的路由方案,它把路由做成了ArkUI的容器组件:Navigation容器内部维护一个NavPathStack路径栈,页面本身是以NavDestination组件为单位存在的。也就是说,一个页面不再是一个独立的Page,而是挂载在Navigation容器里的一个NavDestination节点。
我把两者的核心差异整理成一张表,方便对照理解:
| 对比项 | router模块 | Navigation |
|---|---|---|
| API风格 | 命令式url跳转 | 容器 + 路径栈 |
| 页面载体 | Page | NavDestination组件 |
| 跨模块跳转 | 需要明确依赖和url配置 | 系统路由表 + 动态import,天然支持har/hsp |
| 转场动画 | 系统默认,定制能力有限 | 可精确控制转场,能力丰富 |
| 生命周期 | Page级事件 | NavDestination级事件,更贴近组件生命周期 |
| 参数传递 | 序列化传递,类型约束弱 | 参数对象直接传递,支持返回结果回调 |
| 官方定位 | 旧方案,维护保留 | 当前推荐方案,持续演进 |
为什么鸿蒙会有两个方案?说到底还是迭代节奏的问题。早期为了快速铺开生态,router这种简单的API方案能帮助开发者快速上手。但真正组件化、模块化的大型工程里,router的url字符串跳转有几个硬伤:第一,目标页面url写错是编译期发现不了的,运行时才会炸;第二,跨模块跳转要处理依赖,拆包很痛苦;第三,页面和容器之间的转场和动画定制能力弱。
1.2 为什么新项目我建议直接用Navigation
理由其实就三条,每一条都踩在大型工程的痛点上。
第一,组件化和动态加载。现代应用不可能全塞在一个模块里,拆成har、hsp是常态。Navigation配合系统路由表,可以做到主工程不直接依赖子模块的代码,通过路由名加动态import去加载目标页面。这种模式对团队并行开发极其友好,主工程只管壳和路由表,业务模块独立开发独立发版。这是Router做不到的,Router的url跳转在跨har时依赖关系太重,拆包拆到后面会很痛苦。
第二,类型安全和参数模型。Navigation的pushPath是对象化的,path name和param一起传,你可以在编译期约束参数类型。Router那套url加params本质上都是字符串或者JSON,传参出问题的几率高很多。举个实际例子:详情页要一个商品id和一个来源标记,用Navigation你可以定义一个 DetailParams interface,IDE会帮你校验字段;用Router你只能靠运行时判断,一不小心传错了字段名,目标页拿到的就是undefined。
第三,生命周期更完整、可控。NavDestination的生命周期和组件生命周期挂钩,你能在页面每次显示、隐藏的时机做精确处理。这个能力在做“返回刷新”“状态保持”这类需求时特别有用,后面第三章、第四章会详细展开。
如果项目里已经有大量Router代码怎么办?我的建议是不要一次性推倒重来,把新页面统一走Navigation,老页面逐步换。鸿蒙官方对Router是“保留但不再主推”的态度,短期内还能用,但新特性肯定都只给Navigation。
有人可能会说Navigation入门门槛比Router高,因为引入了容器、路径栈、NavDestination这些概念。我的看法是:你把Navigation容器理解成手机浏览器,NavPathStack就是里面一层层叠着的标签页,pushPath是开新页,pop是返回上一页,popToName是退回到某个指定的页。想清楚这个栈模型,再看代码就不懵了。我面试时爱问一个问题:连续跳了五个页面,怎么一次回到第一个?会栈操作的人秒答popToName,不理解栈的人会开始写循环。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Navigation路由实战:从一个能跑的跨页跳转开始
理论说太多没用,先搭一个最小可运行的项目。我这里用ArkTS写Demo,API版本按API 11以上来,你现在用DevEco Studio新版本建工程,默认就是这套。看代码之前你只需要记住一件事:Navigation的所有跳转,本质上都是对NavPathStack这个路径栈的操作。
2.1 最小可运行配置:容器、路径栈、目标页
第一步,在入口页里放Navigation组件,并创建NavPathStack。
typescript复制// HomePage.ets
@Entry
@Component
struct HomePage {
// 创建一个路径栈,所有路由操作都通过它来
private pathStack:
