前言
在HarmonyOS应用开发中,高效的页面导航与转场管理是构建流畅用户体验的核心要素。为满足不同场景下的开发需求,HarmonyOS提供了两套互补的路由方案:基于Router
模块的标准化页面路由 与基于Navigation
组件的声明式导航系统。传统路由(@ohos.router)通过URL路径映射实现跨页面跳转,适合轻量级场景;而组件导航(Navigation)以ArkUI声明式语法为核心,支持在组件层级进行精细化跳转控制,其特性优势显著。下面就浅聊一下:
一、基于 Router
模块的传统路由
特性与功能
-
两种跳转模式:
router.pushUrl()
:将目标页面压入页面栈,保留当前页面状态,可通过返回键或router.back()
返回原页面,返回后之前的目标页出栈。router.replaceUrl()
:用目标页面替换当前页面,销毁当前页面资源,无法返回原页面。- 页面栈管理 :最大容量为 32 个页面,超出需调用
router.clear()
清空栈以释放内存router.getLength()
获取当前页面栈长度。
-
参数传递与接收:
- 通过
params
字段传递参数,目标页面通过router.getParams()
接收。 - 示例代码:
- 通过
ts
// 父页面传递参数
router.pushUrl({ url: 'pages/Detail', params: { id: 123 } });
// 子页面接收参数
@State id: number = router.getParams()['id'];
-
路由模式
- standard(标准实例模式)
- Single(单实例模式)
不同模式的决定了页面是否会创建多个实例:
-
Standard:无论之前是否添加过,一直添加到页面栈【默认】
场景: 适用于每次跳转都呈现全新内容或状态的场景,避免数据展示紊乱(数据变化)
-
Single:如果之前加过页面,会使用之前添加的页面【需要添加参数手动修改】
场景: 适用于那些需要保留页面状态或避免重复创建相同页面的场景(数据不变化)
路由模式语法:
Ts
Button('跳转')
.onClick(() => {
router.pushUrl({url:'pages/Detail',params:{id:4090} },
router.RouterMode.Single) //单实例模式
})
- 页面状态管理:
- 支持获取当前页面栈信息(如索引、路径)和页面生命周期事件(如
onPageShow
)。
生命周期时序
基于 Router
模块的代码示例
1.基本页面跳转与参数传递
Ts
// 页面A:跳转到页面B,并传递参数
import router from '@ohos.router';
@Entry
@Component
struct PageA {
build() {
Column() {
Button('跳转到PageB')
.onClick(() => {
// 使用pushUrl跳转,保留当前页面
router.pushUrl({
url: 'pages/PageB',
params: { userId: '123', userName: 'HarmonyOS' }
}, router.RouterMode.Standard);
})
}
}
}
// 页面B:接收参数
@Entry
@Component
struct PageB {
@State userId: string = '';
@State userName: string = '';
//如果是需要参数进行网络请求数据则使用aboutToAppear
onPageShow() {
// 通过router.getParams()获取参数
const params = router.getParams();
this.userId = params['userId'];
this.userName = params['userName'];
}
build() {
Column() {
Text(`用户ID: ${this.userId}`)
Text(`用户名: ${this.userName}`)
Button('返回')
.onClick(() => {
router.back(); // 返回上一页
})
}
}
}
- 替换当前页面(无返回栈)
ts
// 使用replaceUrl替换当前页面
router.replaceUrl({
url: 'pages/LoginPage' // 登录后销毁原页面,无法返回
});
- 清空页面栈
ts
// 清空所有页面栈,跳转到首页
router.clear();
router.pushUrl({ url: 'pages/HomePage' });
二、基于 Navigation
组件的进阶路由
定义:
Navigation路由相关的操作都是基于页面栈NavPathStack提供的方法进行,每个Navigation都需要创建并传入一个NavPathStack对象,用于管理页面。主要涉及页面跳转、页面返回、页面替换、页面删除、参数获取、路由拦截等功能
特性与功能
-
核心优势:
- 高效性能:参数传递时性能更优,支持组件动态加载。
- 灵活的路由栈管理 :通过
NavPathStack
管理路由栈,无页面数量限制。 - 丰富的转场动画 :支持默认转场和自定义动画,如共享元素转场(通过
geometryTransition
实现元素联动)。
-
功能扩展:
- 跨模块跳转:支持导入其他模块的页面组件,实现模块解耦。
- 弹窗路由 :通过
NavDestinationMode.DIALOG
实现模态对话框的路由嵌套。 - 生命周期管理 :提供
aboutToAppear
、onAppear
等精细控制页面状态。
-
参数传递与接收:
-
通过
NavPathInfo
对象传递参数,目标页面在onReady
生命周期中接收。 -
示例代码:
-
ts
// 发起页传递参数
let param = { key: 'value' };
this.pageStack.pushDestination(new NavPathInfo('targetPage', param));
// 目标页接收参数
build() {
NavDestination().onReady(cxt => {
this.param = cxt.pathInfo.param;
});
}
- 路由操作
Navigation路由相关的操作都是基于页面栈NavPathStack提供的方法进行,每个Navigation都需要创建并传入一个NavPathStack对象,用于管理页面。主要涉及页面跳转、页面返回、页面替换、页面删除、参数获取、路由拦截等功能。
markdown
* 页面跳转:使用pushPath或pushPathByName打开一个新页面
* 页面返回:使用pop、popToName或popToIndex返回上一个页面或返回到指定页面
* 页面替换:使用replacePath或replacePathByName替换当前页面
* 页面删除:使用removeByName或removeByIndexes删除指定页面
- 基于
Navigation
组件的代码示例
跨模块跳转
- 在跳转目标模块的配置文件module.json5添加路由表配置:
ts
{
"module": {
"name": "home",
"type": "shared",
"description": "$string:shared_desc",
"deviceTypes": [
"phone",
"tablet",
"2in1"
],
"deliveryWithInstall": true,
"pages": "$profile:main_pages",
"routerMap": "$profile:route_map"
}
}
- 添加完路由配置文件地址后,需要在工程resources/base/profile中创建route_map.json文件。添加如下配置信息:
ts
{
"routerMap": [
{
"name": "PageOne",
"pageSourceFile": "src/main/ets/pages/PageOne.ets",
"buildFunction": "PageOneBuilder",
"data": {
"description" : "this is PageOne"
}
}
]
}
- 在跳转目标页面中,需要配置入口Builder函数,函数名称需要和route_map.json配置文件中的buildFunction保持一致,否则在编译时会报错。
ts
// 跳转页面入口函数
@Builder
export function PageOneBuilder() {
PageOne()
}
@Component
struct PageOne {
pathStack: NavPathStack = new NavPathStack()
build() {
NavDestination() {
}
.title('PageOne')
.onReady((context: NavDestinationContext) => {
this.pathStack = context.pathStack
})
}
}
共享元素转场动画
- 为需要实现共享元素转场的组件添加geometryTransition属性,参数必须在两个NavDestination之间保持一致。
ts
Image($r('app.media.mm'))
.geometryTransition('sharedId')
.width(100)
.height(100)
- 将页面路由的操作,放到animateTo动画闭包中,配置对应的动画参数以及关闭系统默认的转场。
ts
Button('跳转到详情页')
.onClick(() => {
this.getUIContext()?.animateTo({
duration: 1000,
curve: Curve.EaseIn,
iterations: 1,
playMode: PlayMode.Alternate,
onFinish: () => {
console.log('动画结束')
}
}, () => {
this.navStack.pushPathByName('DetailPage', new Object({ id: 123, title: 'HarmonyOS' }), false);
})
})
- 效果实现完整代码
ts
import { DetailPage } from './DetailPage';
@Entry
@Component
struct Index {
@Provide('navStack') navStack: NavPathStack = new NavPathStack();
// 自定义动画
pageTransition() {
// Push入场:从底部滑入
PageTransitionEnter({ type: RouteType.Push, duration: 1000, curve: Curve.EaseOut })
.translate({ y: '100%' })
.scale({ x: 0.8, y: 0.8 })
.opacity(0.7);
// Pop退场:向右滑出
PageTransitionExit({ type: RouteType.Pop, duration: 800, curve: Curve.EaseIn })
.translate({ x: '100%' })
.opacity(0);
// Push退场:向左滑出
PageTransitionExit({ type: RouteType.Push, duration: 800 })
.translate({ x: '-100%' });
// Pop入场:从左侧滑入
PageTransitionEnter({ type: RouteType.Pop, duration: 1000 })
.slide(SlideEffect.Left);
}
build() {
Navigation(this.navStack) {
Column({ space: 20 }) {
Image($r('app.media.mm'))
.geometryTransition('sharedId')
.width(100)
.height(100)
Button('跳转到详情页')
.onClick(() => {
this.getUIContext()?.animateTo({
duration: 1000,
curve: Curve.EaseIn,
iterations: 1,
playMode: PlayMode.Alternate,
onFinish: () => {
console.log('动画结束')
}
}, () => {
this.navStack.pushPathByName('DetailPage', new Object({ id: 123, title: 'HarmonyOS' }), false);
}
)
})
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
.title('首页')
.titleMode(NavigationTitleMode.Mini)
.mode(NavigationMode.Stack)
.navDestination(this.routeMap)
}
@Builder
routeMap(name: string, params: object) {
if (name === 'DetailPage') {
DetailPage(params) // ✅ 正确传递参数
}
}
}
interface PageParams {
id: string
title: string
}
@Component
export struct DetailPage {
@Consume('navStack') navStack: NavPathStack;
@State private detailParams: PageParams = { id: '', title: '' };
aboutToAppear() {
const params = this.navStack.getParamByIndex(0) as PageParams;
if (params) this.detailParams = params;
}
build() {
NavDestination() {
Column() {
Image($r('app.media.mm'))
.geometryTransition('sharedId')
.width(200)
.height(200)
Text(this.detailParams.title || '详情页')
.fontSize(24)
.margin({ top: 20 })
Button('返回')
.onClick(() => {
this.getUIContext()?.animateTo({
duration: 1000,
curve: Curve.EaseOut,
iterations: 1,
playMode: PlayMode.Alternate,
onFinish: () => {
console.log('动画结束')
}
}, () => {
this.navStack.pop(false)
})
})
}
}
.title('详情页')
}
}
效果图:
弹窗路由模式
NavDestination
设置mode为NavDestinationMode.DIALOG
弹窗类型,此时整个NavDestination默认透明显示。弹窗类型的NavDestination显示和消失时不会影响下层标准类型的NavDestination的显示和生命周期,两者可以同时显示。
ts
import { DialogPage } from './DialogPage'
//主页面
@Entry
@Component
struct Index {
// 创建一个页面栈对象并传入Navigation
@Provide('NavPathStack') pageStack: NavPathStack = new NavPathStack()
@Builder
PagesMap(name: string) {
if (name == 'DialogPage') {
DialogPage()
}
}
build() {
//传入对象
Navigation(this.pageStack) {
Button('Push DialogPage')
.margin(20)
.width('80%')
.onClick(() => {
this.pageStack.pushPath({ name: "DialogPage", param: new Object({ id: '123abc' })})
})
}
.mode(NavigationMode.Stack) // 默认为Stack
.title('Main')
.navDestination(this.PagesMap ) // 子页面
}
}
//弹框页面
interface PageParams {
id: string
}
@Entry
@Component
export struct DialogPage {
@Consume('NavPathStack') pageStack: NavPathStack;
@State msg:PageParams[]=[]
aboutToAppear(){
this.msg = this.pageStack.getParamByName("DialogPage") as PageParams[]
}
build() {
NavDestination() {
Stack({ alignContent: Alignment.Center }) {
Column() {
Text(this.msg[0].id)
.fontSize(20)
.margin({ bottom: 100 })
Button("Close").onClick(() => {
this.pageStack.pop()
}).width('30%')
}
.justifyContent(FlexAlign.Center)
.backgroundColor(Color.White)
.borderRadius(10)
.height('30%')
.width('80%')
.height("100%").width('100%')
}
.backgroundColor('rgba(0,0,0,0.5)')
.hideTitleBar(true)
.mode(NavDestinationMode.DIALOG) //设置为弹框模式
}
}
效果图:
适用场景
- 复杂应用架构:如多模块协作、跨模块页面跳转。
- 高性能需求场景:如游戏加速(延迟降低 20%)、直播/视频流优化。
- 动态路由管理:需灵活操作路由栈(如清空历史页面、自定义转场逻辑)。
- 高级交互设计:如共享元素转场、弹窗内嵌路由。
三、Router
与 Navigation
的对比
对比维度 | Router 模块 |
Navigation 组件 |
---|---|---|
易用性 | 简单易上手,适合基础跳转和传参。 | 需要理解路由栈管理,适合复杂场景。 |
功能性 | 支持基本跳转和页面栈管理,功能较为基础。 | 支持跨模块跳转、弹窗路由、动态加载组件等高级功能。 |
性能 | 参数传递性能一般,适合轻量级应用。 | 参数传递性能优化,适合高频率或大数据量场景。 |
路由栈限制 | 页面栈最大容量为 32,需手动清理。 | 无数量限制,支持动态管理。 |
生命周期控制 | 提供基础生命周期事件(如 onPageShow )。 |
支持细粒度生命周期(如 aboutToAppear )。 |
转场动画 | 仅支持默认转场效果。 | 支持自定义转场动画和共享元素联动。 |
四、选择建议
-
Router
适用场景:- 快速开发简单应用,如工具类 App、表单提交流程。
- 需要兼容旧版本 HarmonyOS 的项目。
-
Navigation
适用场景:- 大型复杂应用,如电商平台、社交 App。
- 需要多端适配(手机、平板、智能设备)的场景。
- 对性能、动画效果或动态路由有较高要求的项目。
五、扩展功能与最佳实践
- 自适应场景优化 : HarmonyOS 4.0 的
Navigation
支持智能识别用户场景(如游戏、直播),动态分配网络资源,提升体验。 - 路由安全 :可通过
RouterManager.setInterception
拦截非法跳转,或在页面返回时添加弹窗确认逻辑 - 迁移建议 :若原有项目基于
Router
,可逐步将核心页面改造为Navigation
,利用customNavContentTransition
适配转场动画。