【学习目标】

  • 掌握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 实现逻辑

  1. 首页创建NavPathStack,绑定到Navigation
  2. 使用@Builder配置页面映射,通过名称判断进入哪个页面。
  3. 调用pushPathByName实现跳转。
  4. 子页面必须用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%');
  }
}
运行效果

navgation

八、内容总结

  1. 导航体系核心由三部分构成:Navigation根容器、NavPathStack路由栈、NavDestination子页面容器,三者分工明确,共同完成页面导航能力。
  2. NavPathStack是路由逻辑的核心,最常用的接口为:pushPathByName(跳转传参)、pop(返回回传)、popToName(指定页面返回)、clear(清空栈返回首页)、replacePathByName(替换页面)。
  3. 子页面通过onReady回调接收跳转参数和路由栈实例,通过onBackPressed实现返回事件拦截。
  4. 系统路由通过navDestination做页面映射,适合小型项目;中大型项目推荐使用自定义路由表,实现模块解耦和按需加载。

九、代码仓库

  • 工程名称:NavigationDemo
  • 仓库地址:https://gitee.com/HarmonyOS-UI-Basics/harmony-os-ui-basics.git

十、下节预告

下一节我们将学习鸿蒙应用开发中列表布局核心 —— List 组件核心详解,掌握分组列表、粘性吸顶、滚动控制、并完成微信联系人示例。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐