深入解读 HarmonyOS NEXT 声明式路由:NavRouter + NavDestination 实战


在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一、引言

HarmonyOS NEXT 是华为完全剥离 Android 代码、基于 OpenHarmony 演进的纯血操作系统。从 API 24 开始,ArkUI 框架全面转向声明式 UI 范式,其中页面路由是应用架构中最基础也最关键的能力。

过去,开发者习惯使用 router.pushUrl() 命令式跳转——每个页面独立运行在不同上下文,传参和回调需要大量样板代码。在 API 24 中,推荐的做法是声明式路由:所有页面集中在一个组件树中声明,由框架自动完成创建、显示、隐藏与销毁。

本文将从一个可运行的完整示例出发,逐层拆解 NavRouter + NavDestination 的技术细节和实践要点。


二、声明式路由整体架构

声明式路由的核心组件是**Navigation**,它像一个舞台管理者:

┌─────────────────────────────────────┐
│             Navigation               │
│  ┌──────────┬────────────────────┐  │
│  │  导航栏   │   NavDestination   │  │
│  │  ┌────┐  │   目标页面内容     │  │
│  │  │首页 │  │                   │  │
│  │  │个人 │  │                   │  │
│  │  │设置 │  │                   │  │
│  │  └────┘  │                   │  │
│  └──────────┴────────────────────┘  │
└─────────────────────────────────────┘

四个核心角色:

组件 职责
Navigation 路由外壳容器
NavRouter 可点击的导航触发器
NavDestination 目标页面容器
NavPathStack 页面栈管理器

关键区别:声明式路由不创建新 Page 实例,由 Navigation 在同一组件树中按需构建和切换子页面,切换动画由框架免费提供。


三、NavPathStack——路由的「大脑」

NavPathStack可观察的状态容器,管理页面栈中的每个条目。

3.1 创建

@Entry
@Component
struct Index {
  @State
  pathStack: NavPathStack = new NavPathStack();
}

@State 保证栈变化时 Navigation 自动重新渲染。

3.2 核心 API

方法 功能
pushPath(info) 推入一个页面
pushPathByName(name, param) 按名称推入,可携带参数
pop() 返回上一页
popToName(name) 弹到指定名称
popToIndex(index) 弹到指定索引
clear() 清空栈
getAllPathName() 获取所有页面名称
size() 获取栈深度

示例中,每个 NavRouter 点击时调用:

.onClick(() => {
  this.pathStack.pushPath({ name: 'HomePage' });
})

四、Navigation——外壳容器配置

4.1 模式

Navigation(this.pathStack)
  .mode(NavigationMode.Stack)
模式 行为 适用场景
Stack 导航栏 + 页面栈 侧边栏 + 内容区
Split 分栏并排 平板、大屏
Auto 自适应 多端适配

4.2 导航栏

Navigation(this.pathStack)
  .title('导航菜单')
  .navBarWidth('100%')

4.3 navDestination——核心映射

Navigation(this.pathStack) { /* 导航栏内容 */ }
  .navDestination(this.pageMap)

navDestination 接收 @Builder 函数,框架在每次 pushPath 时根据 name 创建对应 NavDestination


五、@Builder 页面映射详解

@Builder 扮演路由表的角色:

@Builder
pageMap(name: string, param: Object) {
  if (name === 'HomePage') {
    NavDestination() {
      HomePage({ pathStack: this.pathStack })
    }
    .title('首页')
    .backgroundColor('#f5f5f5')
    .onShown(() => console.info('首页已显示'))
    .onHidden(() => console.info('首页已隐藏'))
  } else if (name === 'ProfilePage') {
    NavDestination() { ProfilePage() }
      .title('个人中心')
  }
}

参数传递

HomePage({ pathStack: this.pathStack })

子页面可通过 this.pathStack?.pop() 手动返回,比 router.back() 更灵活。

生命周期钩子

钩子 触发时机
onShown 页面显示时
onHidden 页面隐藏时
onAppear 组件挂载时
onDisAppear 组件卸载时

声明式路由的页面是「显示/隐藏」而非「创建/销毁」,切换轻量,状态自然保持。


六、NavRouter 详解

NavRouter 是用户导航的入口,API 24 中已标记 deprecated,推荐用 Row + onClick 替代。

6.1 示例

NavRouter() {
  Row() {
    Text('🏠').fontSize(24)
    Column() {
      Text('首页').fontSize(16)
      Text('应用主页面').fontSize(12).fontColor('#999')
    }
  }
  .padding(20)
}
.onClick(() => {
  this.pathStack.pushPath({ name: 'HomePage' });
})

6.2 推荐替代

Row() { Text('导航项') }
  .onClick(() => this.pathStack.pushPath({ name: 'TargetPage' }))

优势:样式自由、无嵌套开销。


七、NavDestination 详解

7.1 基本使用

NavDestination() {
  HomePage()
}
.title('首页')
.backgroundColor('#f5f5f5')

7.2 标题栏控制

.hideTitleBar(true)
.backButtonIcon($r('app.media.back'))
.onBackPressed(() => false) // true 消费返回事件

7.3 内置转场动画

框架自动为 NavDestination 进出应用平滑动画,无需额外配置。


八、示例架构分析

8.1 文件组织

所有逻辑集中在 Index.ets。真实项目推荐拆分:

pages/
├── Index.ets          ← 主入口 + @Builder pageMap
├── HomePage.ets       ← 首页
├── ProfilePage.ets    ← 个人中心
├── SettingsPage.ets   ← 系统设置
└── AboutPage.ets      ← 关于

8.2 状态分层

层级 状态 管理方式
路由级 pathStack Index 的 @State
页面级 message / isNotify / isDark 各子组件 @State

8.3 参数传递路径

Index (NavPathStack)
  ├─ pushPath('HomePage') → HomePage({ pathStack })
  │    └─ pathStack?.pop()
  ├─ pushPath('ProfilePage') → ProfileRow({ label, value })
  └─ pushPath('SettingsPage') → Toggle {...}

九、子组件与 @Prop 传参

@Component
struct ProfileRow {
  @Prop label: string = '';
  @Prop value: string = '';

  build() {
    Row() {
      Text(this.label).fontSize(14).fontColor('#666')
      Text(this.value).fontSize(14).fontColor('#333')
    }
    .width('100%')
    .justifyContent(FlexAlign.SpaceBetween)
    .padding({ top: 10, bottom: 10 })
  }
}

关键:必须使用对象字面量传参:

// ✅ 正确
ProfileRow({ label: '用户名', value: 'AtomCode' })

// ❌ 编译错误
ProfileRow('用户名', 'AtomCode')

十、命令式 vs 声明式路由

10.1 代码量对比

命令式

router.pushUrl({ url: 'pages/Second', params: { key: 'value' } });
// 目标页用 router.getParams() 读取

声明式(集中声明):

this.pathStack.pushPath({ name: 'Second', param: { key: 'value' } });
NavDestination() { SecondPage({ data: param['key'] }) }

10.2 状态保持

关注点 命令式 声明式
页面切换 新建 Page 实例 隐藏/显示 NavDestination
状态保持 手动管理 天然保持
动画过渡 手动配置 内置
传参 getParams() 全局读取 组件参数传入

10.3 适用场景

声明式:侧边栏布局、多 Tab 切换、页面间紧密交互。

命令式:独立页面(登录→主页)、Deep Link、跨 Ability。


十一、最佳实践

11.1 pageMap 优化

超过 10 个页面用 switch

@Builder
pageMap(name: string, param: Object) {
  switch (name) {
    case 'HomePage': break;
    case 'ProfilePage': break;
    default: // 404
  }
}

11.2 返回时状态重置

NavDestination()
  .onAppear(() => { this.isNotify = true; this.isDark = false; })

11.3 栈深度控制

建议不超过 10 层。过深时用 popToName 清理中间层。


十二、API 12 → API 24 迁移

API 版本 用法
API 12 NavRouter().destination() 链式声明
API 24 NavRouter deprecated,推荐 onClick + @Builder

迁移步骤

  1. NavRouterRow + onClick
  2. .destination() 中 NavDestination → 移到 @Builder pageMap
  3. pushPathByNamepushPath
  4. 利用新增生命周期和动画属性

十三、进阶:自定义过渡动画

Navigation(this.pathStack).transition({
  duration: 350,
  curve: Curve.FastOutSlowIn,
  type: NavigationTransitionType.Push,
  slide: { edge: SlideEdge.Right, distance: '100%' }
})

NavDestination 级别:

NavDestination().transition({
  enter: TransitionEffect.slide(SlideEdge.Right),
  exit: TransitionEffect.fade
})

十四、总结

维度 评价
学习曲线 中等,API 设计直观
可维护性 优秀,集中声明,状态流清晰
性能 良好,按需构建,内置动画
灵活性 较高,生命周期、动画均可定制
适用规模 中小到大型应用均可

核心思想:状态驱动视图而非「跳转命令」。当你在 @Builder pageMap 中声明 NavDestination 时,你是在为应用的页面关系拓扑编程,而非编排跳转序列。


附录:快速体验

  1. DevEco Studio 中新建 HarmonyOS NEXT 项目(API 24)
  2. 替换 Index.ets 为完整示例代码
  3. 运行应用,点击菜单项观察页面切换
Logo

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

更多推荐