《多级标签并行筛选》三、Navigation导航使用指南
HarmonyOS ArkUI Navigation 导航组件从入门到实战完整指南
本文全面介绍 HarmonyOS ArkUI 中 Navigation 导航组件的架构设计、核心 API、路由配置和页面生命周期管理,并通过完整示例演示实际开发流程,帮助开发者构建规范的页面导航体系。
效果
一、前言
在 HarmonyOS 应用开发中,页面导航是连接各个功能模块的桥梁。传统开发中使用 router 模块进行页面跳转,但随着应用复杂度提升,router 在路由管理、参数传递、生命周期控制、转场动画等方面的局限性日益明显。
Navigation 是 ArkUI 推出的新一代导航框架,它提供了声明式的导航架构,集成了页面栈管理、路由注册、生命周期回调、转场动画等能力,是当前官方推荐的标准导航方案。
重要提示:
router模块已被标记为废弃,新项目应直接使用 Navigation。
二、Navigation 架构概览
Navigation 采用三层架构:
Navigation(根容器)
├── NavBar(导航栏:标题、菜单、工具栏)
├── NavDestination(子页面容器,各自独立生命周期)
└── NavPathStack(管理页面栈状态和转场)
2.1 核心组件职责
| 组件 | 职责 |
|---|---|
| Navigation | 导航根容器,管理导航栏和页面展示区域 |
| NavDestination | 子页面容器,每个目标页面使用 NavDestination 包裹 |
| NavPathStack | 页面栈管理器,控制 push/pop/replace 等操作 |
2.2 导航模式
| 模式 | 适用场景 | 说明 |
|---|---|---|
| Stack | 屏幕宽度 < 600vp | 整页替换,类似手机页面跳转 |
| Split | 屏幕宽度 >= 600vp | 左侧导航栏 + 右侧内容区,类似平板双栏 |
| Auto | 自适应 | 系统根据屏幕宽度自动切换 |
三、基础用法
3.1 最简单的导航结构
@Entry
@Component
struct AppEntry {
@Provide('navStack') pathStack: NavPathStack = new NavPathStack();
build() {
Navigation(this.pathStack) {
// NavBar 内容(首页)
Column() {
Text('首页')
.fontSize(24)
.fontWeight(FontWeight.Bold)
Button('前往详情')
.onClick(() => {
this.pathStack.pushPath({
name: 'DetailPage',
param: { id: 1 }
});
})
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
.title('应用标题')
.navDestination(this.pageBuilder)
}
@Builder
pageBuilder(name: string, param: Object) {
NavDestination() {
DetailPage()
}
}
}
3.2 NavPathStack 页面栈操作
NavPathStack 是导航的核心控制器,常用操作如下:
// 压入新页面(推荐,支持路由参数)
this.pathStack.pushPath({ name: 'DetailPage', param: data });
// 按名称压入(简化写法)
this.pathStack.pushPathByName('DetailPage', paramData);
// 弹出当前页面
this.pathStack.pop();
// 弹出并传回结果
this.pathStack.pop({ result: '操作成功' });
// 替换当前页面
this.pathStack.replacePath({ name: 'OtherPage', param: data });
// 弹出到指定页面
this.pathStack.popToName('HomePage');
// 弹出到指定索引
this.pathStack.popToIndex(0);
// 清空页面栈
this.pathStack.clear();
// 获取栈大小
this.pathStack.getSize();
// 获取当前索引
this.pathStack.getCurrentIndex();
3.3 NavPathStack 方法速查表
| 方法 | 说明 |
|---|---|
pushPath(info) |
压入目标页面,可携带参数 |
pushPathByName(name, param) |
按名称压入,简化写法 |
pop(param?) |
弹出当前页,可传回结果 |
popToName(name) |
弹出到指定名称的页面 |
popToIndex(index) |
弹出到指定索引的页面 |
replacePath(info) |
替换当前页面 |
clear() |
清空页面栈 |
getAllPathName() |
获取栈中所有页面名称 |
getParamByIndex(index) |
获取指定索引的参数 |
getCurrentIndex() |
获取当前页面索引 |
getSize() |
获取页面栈大小 |
四、路由配置
4.1 静态路由(route_map.json)
在 src/main/resources/base/profile/ 下创建 route_map.json:
{
"routerMap": [
{
"name": "HomePage",
"pageSourceFile": "src/main/ets/pages/HomePage.ets",
"buildFunction": "HomePageBuilder"
},
{
"name": "DetailPage",
"pageSourceFile": "src/main/ets/pages/DetailPage.ets",
"buildFunction": "DetailPageBuilder"
}
]
}
4.2 页面注册
在每个页面文件中导出 @Builder 函数:
// HomePage.ets
@Builder
export function HomePageBuilder() {
HomePage();
}
@Entry
@Component
struct HomePage {
build() {
NavDestination() {
Text('首页内容')
}
}
}
4.3 module.json5 配置
{
"module": {
// ...
"pages": "$profile:main_pages",
"routerMap": "$profile:route_map"
}
}
4.4 配置应用启动页面
应用的启动页面由 EntryAbility.ets 中的 windowStage.loadContent() 决定,而非 main_pages.json 中的页面顺序。
// entry/src/main/ets/entryability/EntryAbility.ets
onWindowStageCreate(windowStage: window.WindowStage): void {
// 修改此处参数即可切换启动页面
windowStage.loadContent('pages/MovieFilterPage', (err) => {
if (err.code) {
hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
return;
}
hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.');
});
}
常见误区:仅修改
main_pages.json中的页面顺序并不能改变启动页面。main_pages.json仅用于页面路由注册,真正决定启动页面的是EntryAbility.ets中loadContent的路径参数。
五、页面生命周期
NavDestination 提供完整的生命周期回调:
5.1 生命周期回调
NavDestination() {
Column() {
Text('页面内容')
}
}
.onReady((ctx: NavDestinationContext) => {
// 页面创建并准备好时调用
// 可通过 ctx.pathInfo.param 获取传入参数
const param = ctx.pathInfo.param;
})
.onShown(() => {
// 页面变为可见时调用
})
.onHidden(() => {
// 页面变为不可见时调用
})
.onBackPressed(() => {
// 返回键按下时调用
// 返回 true 拦截默认返回,false 允许默认返回
return false;
})
5.2 生命周期顺序
页面首次进入:onReady → onShown
页面被覆盖(push新页面):onHidden
页面重新显示(pop回来):onShown
页面即将销毁:onHidden → 销毁
返回键按下:onBackPressed
六、参数传递
6.1 传递简单数据
// 传递
this.pathStack.pushPath({
name: 'DetailPage',
param: 'Hello World'
});
// 接收(在 NavDestination 的 onReady 中)
.onReady((ctx: NavDestinationContext) => {
const message = ctx.pathInfo.param as string;
})
6.2 传递复杂对象
interface ProductInfo {
id: string;
name: string;
price: number;
}
// 传递
this.pathStack.pushPath({
name: 'ProductDetail',
param: {
id: 'p001',
name: '示例商品',
price: 99.9
} as ProductInfo
});
// 接收
.onReady((ctx: NavDestinationContext) => {
const product = ctx.pathInfo.param as ProductInfo;
})
6.3 最佳实践:传递 ID,按需加载
// 推荐:传递 ID,在目标页面按需加载完整数据
this.pathStack.pushPath({
name: 'ProductDetail',
param: productId // 只传 ID
});
// 避免:传递整个大对象
this.pathStack.pushPath({
name: 'ProductDetail',
param: largeProductObject // 不推荐
});
七、Navigation 配置
7.1 标题栏
Navigation(this.pathStack) {
// ...
}
.title('页面标题')
.titleMode(NavigationTitleMode.Mini) // 紧凑标题
.titleSubtitle({
title: '主标题',
subtitle: '副标题'
})
7.2 隐藏标题栏和工具栏
Navigation(this.pathStack) {
// ...
}
.hideTitleBar(true)
.hideToolBar(true)
7.3 菜单按钮
Navigation(this.pathStack) {
// ...
}
.menus([
{
value: '搜索',
icon: $r('app.media.search'),
action: () => { /* 搜索逻辑 */ }
},
{
value: '设置',
icon: $r('app.media.settings'),
action: () => { /* 设置逻辑 */ }
}
])
八、转场动画
8.1 默认转场
Navigation 提供默认的滑动转场效果。
8.2 自定义转场
NavDestination() {
Column() { Text('新页面') }
}
.transition(
TransitionEffect.OPACITY.animation({
duration: 300,
curve: Curve.EaseInOut
})
)
8.3 常用转场效果
// 左滑进入
TransitionEffect.SLIDE_LEFT.animation({ duration: 300 })
// 淡入
TransitionEffect.OPACITY.animation({ duration: 200 })
// 组合效果
TransitionEffect.asSequence([
TransitionEffect.OPACITY.scale({ scale: 0.95 }),
TransitionEffect.OPACITY
])
九、完整示例:多页面导航应用
// ============= 首页 =============
@Builder
export function HomeBuilder() {
HomePage();
}
@Entry
@ComponentV2
struct HomePage {
@Provider('appNavStack') pathStack: NavPathStack = new NavPathStack();
build() {
Navigation(this.pathStack) {
Column({ space: 16 }) {
Text('导航示例')
.fontSize(26)
.fontWeight(FontWeight.Bold)
Button('商品列表')
.width(200)
.onClick(() => {
this.pathStack.pushPath({
name: 'ProductList',
param: { category: 'electronics' }
});
})
Button('关于页面')
.width(200)
.onClick(() => {
this.pathStack.pushPathByName('About', null);
})
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
.title('首页')
.navDestination(this.routeBuilder)
}
@Builder
routeBuilder(name: string, param: Object) {
NavDestination() {
if (name === 'ProductList') {
ProductListPage()
} else if (name === 'About') {
AboutPage()
}
}
}
}
// ============= 商品列表页 =============
@Builder
export function ProductListBuilder() {
ProductListPage();
}
@ComponentV2
struct ProductListPage {
@Local products: string[] = ['手机', '电脑', '平板', '耳机'];
build() {
Column({ space: 12 }) {
Text('商品列表')
.fontSize(20)
.fontWeight(FontWeight.Bold)
ForEach(this.products, (product: string) => {
Row() {
Text(product)
.fontSize(16)
Blank()
Text('>')
.fontSize(16)
.fontColor('#CCCCCC')
}
.width('100%')
.padding(16)
.backgroundColor('#FAFAFA')
.borderRadius(8)
}, (product: string) => product)
}
.padding(20)
.width('100%')
.height('100%')
}
}
// ============= 关于页 =============
@Builder
export function AboutBuilder() {
AboutPage();
}
@ComponentV2
struct AboutPage {
build() {
Column() {
Text('关于')
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text('Navigation 导航示例应用 v1.0')
.fontSize(14)
.fontColor('#666666')
.margin({ top: 12 })
}
.justifyContent(FlexAlign.Center)
.width('100%')
.height('100%')
}
}
十、Navigation vs router 对比
| 特性 | Navigation | router(已废弃) |
|---|---|---|
| 架构模式 | 声明式,组件化 | 命令式,URL 跳转 |
| 页面栈管理 | NavPathStack 内置 | 需手动管理 |
| 参数传递 | 支持任意对象 | 仅支持序列化数据 |
| 生命周期 | onReady/onShown/onHidden | 仅 onPageShow/onPageHide |
| 转场动画 | 丰富且可自定义 | 有限 |
| 响应式适配 | 自动支持分栏模式 | 不支持 |
| V2 状态管理 | 完全兼容 | 需额外适配 |
| 官方推荐 | 推荐 | 废弃 |
十一、常见问题
Q1:NavDestination 中如何获取 NavPathStack?
// 方式一:通过 @Provide/@Consume 共享
@Provide('navStack') pathStack: NavPathStack = new NavPathStack();
// 方式二:V2 中使用 @Provider/@Consumer
@Provider('navStack') pathStack: NavPathStack = new NavPathStack();
// 子页面获取
@Consumer('navStack') pathStack: NavPathStack = new NavPathStack();
Q2:如何拦截返回键?
NavDestination() {
// 页面内容
}
.onBackPressed(() => {
// 执行自定义逻辑(如保存数据、确认退出)
if (this.hasUnsavedChanges) {
this.showConfirmDialog();
return true; // 拦截默认返回
}
return false; // 允许默认返回
})
Q3:如何在子页面中操作页面栈?
@ComponentV2
struct ChildPage {
@Consumer('navStack') pathStack: NavPathStack = new NavPathStack();
build() {
NavDestination() {
Button('返回首页')
.onClick(() => {
this.pathStack.clear(); // 清空栈,回到首页
})
Button('返回上一页')
.onClick(() => {
this.pathStack.pop();
})
}
}
}
十二、最佳实践
- 新项目直接使用 Navigation:不要使用已废弃的
router模块。 - 通过
@Provide/@Provider共享 NavPathStack:确保所有页面都能访问导航控制器。 - 传递 ID 而非完整对象:减少内存占用,目标页面按需加载。
- 使用
route_map.json注册路由:便于维护和管理。 - 启动页面由
EntryAbility.ets决定:修改windowStage.loadContent()的参数,而非main_pages.json顺序。 - 在
onBackPressed中处理返回逻辑:如表单保存确认、数据清理等。 - 配合 V2 状态管理使用:
@ComponentV2+@Local/@Param提供更现代的开发体验。 - 隐藏不需要的标题栏:使用
hideTitleBar(true)自定义页面头部。
十三、总结
Navigation 是 HarmonyOS 应用的推荐导航方案,它通过 Navigation(根容器)+ NavDestination(子页面)+ NavPathStack(栈管理) 的三层架构,提供了声明式、可维护、高性能的页面导航能力。掌握 NavPathStack 的栈操作、NavDestination 的生命周期回调和 route_map.json 的路由配置,即可构建出规范、流畅的多页面应用。
参考文档:Navigation
更多推荐




所有评论(0)