鸿蒙应用开发UI基础第二十八节:页面导航Navigation组件与路由栈管理
本文介绍了鸿蒙Navigation导航组件的核心功能与使用方法。主要内容包括: 导航体系三要素:Navigation根容器、NavPathStack路由栈控制器和NavDestination子页面容器 核心组件常用接口表格,详细列出Navigation、NavPathStack和NavDestination的关键属性和方法 基础跳转示例,展示如何创建路由栈、绑定Navigation和实现页面跳转
·
【学习目标】
- 掌握Navigation组件的核心定位、常用属性与配置方法;
- 熟练掌握NavPathStack的核心常用接口,包括跳转、返回、替换、传参等全场景操作;
- 理解NavDestination子页面的核心作用、常用属性与生命周期回调;
- 掌握系统路由表的实现逻辑与实战流程;
- 能够基于核心接口,实现完整的多页面业务流程与导航交互。
一、工程结构(API12+)
NavigationDemo/
├── entry/
│ ├── src/main/
│ │ ├── ets/
│ │ │ ├── pages/ # 页面层
│ │ │ │ ├── Index.ets # 首页(Navigation根容器)
│ │ │ │ ├── NewsDetailPage.ets
│ │ │ │ ├── MineDetailPage.ets
│ │ │ │ └── EditPage.ets
│ │ │ └── user/ # 用户信息模块
│ │ │ └── UserInfo.ets # 用户信息接口定义
│ │ ├── resources/
│ │ │ └── base/
│ │ │ └── profile/
│ │ │ └── router_map.json # 系统路由表配置
│ │ └── module.json5 # 模块配置
│ └── build-profile.json5
└── build-profile.json5
二、导航核心认知
2.1 核心架构三要素
鸿蒙Navigation导航体系,由三个核心部分构成,三者各司其职,共同完成完整的页面导航能力:
- Navigation:导航根容器,必须作为
@Entry入口页面的根节点,主要负责导航栏样式、显示模式、绑定路由栈,是整个导航体系的载体。 - NavPathStack:路由栈控制器,是整个导航的逻辑中枢。本质是页面栈结构,存储访问历史,所有跳转、返回、替换都是对栈的操作。
- NavDestination:子页面根容器,所有二级、三级页面必须以它为根节点。负责页面内容、独立标题、参数接收、生命周期、返回拦截。
三、核心组件常用接口(表格版)
3.1 Navigation 组件(根容器)
| 属性/方法 | 说明 | 可选值/用法 |
|---|---|---|
| 构造函数 | 必须绑定路由栈 | Navigation(this.pageStack) |
title |
导航栏主标题 | 字符串 |
mode |
页面显示模式 | Auto 自适应Stack 单栏Split 分栏 |
titleMode |
标题样式 | Mini 普通Full 大标题Hide 隐藏 |
menus |
右上角菜单 | NavigationMenuItem[] |
navDestination |
静态页面映射 | @Builder 构建函数 |
hideNavBar |
隐藏导航栏 | true / false |
3.2 NavPathStack 路由栈接口(核心)
3.2.1 页面跳转(入栈)
| 接口 | 功能说明 |
|---|---|
pushPathByName(name, param?, callback?) |
按页面名跳转,支持传参+接收返回值 |
pushPath(pathInfo) |
对象方式跳转 |
3.2.2 页面返回(出栈)
| 接口 | 功能说明 |
|---|---|
pop(result?) |
返回上一页,可回传数据 |
popToName(name) |
返回到指定页面,销毁中间页 |
clear() |
清空栈,返回首页 |
3.2.3 高级操作
| 接口 | 功能说明 |
|---|---|
replacePathByName(name) |
替换当前页,不可回退,例如登录成功后不能直接返回 |
size() |
获取栈内页面数量 |
3.3 NavDestination 子页面容器
| 属性/回调 | 说明 | 用法 |
|---|---|---|
title |
子页面标题 | 每个页面独立配置 |
hideTitleBar |
隐藏子页面导航栏 | true / false |
mode |
展示模式 | STANDARD 标准页DIALOG 弹窗 |
.onReady(context) |
页面创建,接收参数 | context.pathInfo.param |
.onBackPressed() |
返回按键拦截 | true拦截 / false放行 |
四、入门示例:基础跳转
4.1 实现逻辑
- 首页创建
NavPathStack,绑定到Navigation。 - 使用
@Builder配置页面映射,通过名称判断进入哪个页面。 - 调用
pushPathByName实现跳转。 - 子页面必须用
NavDestination作为根容器。
4.2 代码实现
步骤1:首页 Index.ets
import { MineDetailPage } from './MineDetailPage';
import { NewsDetailPage } from './NewsDetailPage';
@Entry
@Component
struct Index {
// 路由栈实例
private pageStack: NavPathStack = new NavPathStack();
// 页面映射
@Builder
pageMap(name: string) {
if (name === 'NewsDetailPage') {
NewsDetailPage();
}
}
@Builder homeBuilder(){
Column({ space: 25 }) {
Text('鸿蒙资讯首页')
.fontSize(26)
.fontWeight(FontWeight.Bold);
Button('查看新闻详情')
.width(220)
.height(50)
.borderRadius(25)
.onClick(() => {
this.pageStack.pushPathByName('NewsDetailPage', undefined);
});
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center);
}
build() {
Navigation(this.pageStack) {
this.homeBuilder()
}
.title('首页')
.mode(NavigationMode.Stack)
.navDestination(this.pageMap)
.width('100%')
.height('100%');
}
}
步骤2:新闻详情页 NewsDetailPage.ets
@Component
export struct NewsDetailPage {
build() {
NavDestination() {
Column({ space: 20 }) {
Text('鸿蒙官方资讯')
.fontSize(24)
.fontWeight(FontWeight.Bold);
Text('Navigation 组件全新升级')
.fontSize(18)
.fontColor('#666');
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center);
}
.title('新闻详情');
}
}
五、进阶实战:传递参数+值回调+拦截提示
5.1 定义公共接口(用户信息层)
文件:user/UserInfo.ets
// 用户信息参数
export interface UserInfo {
id: number;
name: string;
level: string;
}
// 修改返回结果
export interface UserUpdateResult {
update: boolean;
newName: string;
time: string;
}
5.2 首页跳转传参(Index.ets)
import { UserInfo, UserUpdateResult } from '../user/UserInfo';
import { MineDetailPage } from './MineDetailPage';
import { NewsDetailPage } from './NewsDetailPage';
@Entry
@Component
struct Index {
private pageStack: NavPathStack = new NavPathStack();
@State resultInfo: string = '';
@Builder
pageMap(name: string) {
if (name === 'NewsDetailPage') {
NewsDetailPage();
}
if (name === 'MineDetailPage') {
MineDetailPage();
}
}
@Builder mineBuilder(){
Column({ space: 25 }) {
Text('个人中心')
.fontSize(26)
.fontWeight(FontWeight.Bold);
Button('进入个人资料')
.width(220)
.height(50)
.borderRadius(25)
.onClick(() => {
// 定义interface类型参数
const userInfo: UserInfo = {
id: 10086,
name: '鸿蒙开发者',
level: '高级'
};
this.pageStack.pushPathByName(
'MineDetailPage',
userInfo,
(popData: PopInfo) => {
const res = popData.result as UserUpdateResult;
this.resultInfo = `修改后:${res.newName} | 时间:${res.time}`;
}
);
});
if (this.resultInfo) {
Text(`返回结果:${this.resultInfo}`)
.fontSize(15)
.fontColor('#007DFF')
.margin({ top: 10 });
}
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center);
}
build() {
Navigation(this.pageStack) {
this.mineBuilder()
}
.title('首页')
.mode(NavigationMode.Stack)
.navDestination(this.pageMap)
.width('100%')
.height('100%');
}
}
5.3 子页面接收参数(MineDetailPage.ets)
import { UserInfo, UserUpdateResult } from "../user/UserInfo";
@Builder
export function MineDetailPageBuilder(userInfo: UserInfo) {
MineDetailPage({ userInfo: userInfo });
}
@Component
export struct MineDetailPage {
@Consume('pathStack') pageStack: NavPathStack;
@Prop userInfo: UserInfo;
private isSaved: boolean = false;
// 弹出提示并处理返回逻辑
popAlert() {
if (!this.isSaved) {
const result: UserUpdateResult = {
update: true,
newName: '鸿蒙高级开发者',
time: new Date().toLocaleString()
};
AlertDialog.show({
title: '温馨提示',
message: '内容尚未保存,确定离开?',
primaryButton: {
value: '取消',
action: () => {}
},
secondaryButton: {
value: '确定离开',
action: () => {
this.pageStack?.pop(result, true);
}
}
});
} else {
// 已保存,直接返回
this.pageStack?.pop();
}
}
build() {
NavDestination() {
Column({ space: 18 }) {
Text(`用户ID:${this.userInfo.id}`).fontSize(18);
Text(`用户名:${this.userInfo.name}`).fontSize(18);
Text(`等级:${this.userInfo.level}`).fontSize(18);
Button('返回并更新资料')
.width(240)
.height(50)
.borderRadius(25)
.onClick(() => {
this.popAlert();
});
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center);
}
.title('个人资料')
.onReady((context: NavDestinationContext) => {
const params = context.pathInfo.param as UserInfo;
this.userInfo = params;
})
.onBackPressed(() => {
// 拦截系统返回并弹出提示框
this.popAlert();
return true;
});
}
}
六、系统路由的局限性
当前Index根页面只有两个子页面,代码量已经较为臃肿。如果项目存在30个甚至更多页面,静态页面映射的方式会导致代码难以维护,无法满足大型项目需求,因此需要使用自定义路由表优化。
Index页面留存,作为学习记录。我们新建一个Main页面 复制Index页面内容并改造。
七、进阶:配置系统路由表
7.1 核心价值
通过配置文件统一管理路由,无需在首页导入所有页面,实现代码解耦,是中大型项目的首选方案。
7.2 实现步骤
步骤1:配置 router_map.json
{
"routerMap": [
{
"name": "MineDetailPage",
"pageSourceFile": "src/main/ets/pages/MineDetailPage.ets",
"buildFunction": "MineDetailPageBuilder"
}
]
}
步骤2:配置 module.json5
{
"module": {
"routerMap": "$profile:router_map"
}
}
步骤3:子页面暴露入口函数(MineDetailPage.ets)
import { UserInfo, UserUpdateResult } from "../user/UserInfo";
// 路由表入口函数,与buildFunction同名
@Builder
export function MineDetailPageBuilder(userInfo:UserInfo) {
MineDetailPage({userInfo:userInfo});
}
@Component
export struct MineDetailPage {
@Consume ('pathStack') pageStack: NavPathStack;
@Prop userInfo:UserInfo;
private isSaved:boolean = false;
popAlert():boolean{
if (!this.isSaved) {
const result: UserUpdateResult = {
update: true,
newName: '鸿蒙高级开发者',
time: new Date().toLocaleString()
};
AlertDialog.show({
title: '温馨提示',
message: '内容尚未保存,确定离开?',
primaryButton: { value: '取消',action:()=>{} },
secondaryButton: {
value: '确定离开',
action: () => this.pageStack?.pop(result,true)
}
});
return true;
}
return false;
}
build() {
NavDestination() {
Column({ space: 18 }) {
Text(`用户ID:${this.userInfo.id}`).fontSize(18);
Text(`用户名:${this.userInfo.name}`).fontSize(18);
Text(`等级:${this.userInfo.level}`).fontSize(18);
Button('返回并更新资料')
.width(240)
.height(50)
.borderRadius(25)
.onClick(() => {
this.popAlert()
});
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center);
}
.title('个人资料')
.onReady((context: NavDestinationContext) => {
const params = context.pathInfo.param as UserInfo;
this.userInfo = params;
})
.onBackPressed(() => {
this.popAlert();
return false;
});
}
}
步骤4:配置页面路由
修改main_pages.json,新增主页面:
{
"src": [
"pages/Index",
"pages/Main"
]
}
步骤5:修改应用加载页面
windowStage.loadContent('pages/Main', (err) => { });
步骤6:简化根页面 Main.ets
import { UserInfo } from '../user/UserInfo';
@Entry
@Component
struct Main {
@Provide ('pathStack') pageStack: NavPathStack = new NavPathStack();
build() {
Navigation(this.pageStack) {
Column({ space: 25 }) {
Text('鸿蒙资讯首页')
.fontSize(26)
.fontWeight(FontWeight.Bold);
Button('查看新闻详情')
.width(220)
.height(50)
.borderRadius(25)
.onClick(() => {
this.pageStack.pushPathByName('NewsDetailPage',undefined);
});
Button('进入个人资料')
.width(220)
.height(50)
.borderRadius(25)
.onClick(() => {
const userInfo:UserInfo = {
id: 10086,
name: '鸿蒙开发者',
level: '高级'
};
this.pageStack.pushPathByName('MineDetailPage', userInfo);
});
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center);
}
.title('首页')
.mode(NavigationMode.Stack)
.width('100%')
.height('100%');
}
}
运行效果

八、内容总结
- 导航体系核心由三部分构成:Navigation根容器、NavPathStack路由栈、NavDestination子页面容器,三者分工明确,共同完成页面导航能力。
- NavPathStack是路由逻辑的核心,最常用的接口为:
pushPathByName(跳转传参)、pop(返回回传)、popToName(指定页面返回)、clear(清空栈返回首页)、replacePathByName(替换页面)。 - 子页面通过
onReady回调接收跳转参数和路由栈实例,通过onBackPressed实现返回事件拦截。 - 系统路由通过
navDestination做页面映射,适合小型项目;中大型项目推荐使用自定义路由表,实现模块解耦和按需加载。
九、代码仓库
- 工程名称:NavigationDemo
- 仓库地址:https://gitee.com/HarmonyOS-UI-Basics/harmony-os-ui-basics.git
十、下节预告
下一节我们将学习鸿蒙应用开发中列表布局核心 —— List 组件核心详解,掌握分组列表、粘性吸顶、滚动控制、并完成微信联系人示例。
更多推荐




所有评论(0)