news 2026/7/24 21:41:47

页面路由导航:Router与Navigation组件的跳转传参(7)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
页面路由导航:Router与Navigation组件的跳转传参(7)
前言:选择正确的导航方案

在鸿蒙 ArkTS 开发中,页面跳转与导航是应用骨架的核心。目前 ArkUI 提供了两套路由方案:

  1. Navigation 组件(推荐):基于组件化的路由容器,适用于绝大多数场景,特别是需要复杂交互、多端适配(一次开发,多端部署)的应用。
  2. Router 模块(不推荐):基于页面路径的跳转方式,功能较基础,页面栈有上限(32层),主要用于简单的页面跳转或兼容旧代码。

本文将重点讲解官方推荐的Navigation组件,并简要对比Router模块。


一、 Navigation 组件:组件级路由的新标准

Navigation是一个路由导航的根视图容器,它支持单栏(Stack)、分栏(Split)和自适应(Auto)三种显示模式,能够根据窗口大小自动切换布局,非常适合折叠屏和平板设备。

核心概念:

  • NavPathStack:导航路径栈,用于管理页面的入栈(Push)、出栈(Pop)和替换(Replace)。
  • NavDestination:子页面容器,必须嵌套在Navigation组件中使用。

1. 基础路由配置与跳转

要实现Navigation路由,首先需要配置路由表,并在module.json5中注册。

步骤一:配置路由表 (router_map.json)
src/main/resources/base/profile目录下创建router_map.json

{ "routerMap": [ { "name": "pageOne", "pageSourceFile": "src/main/ets/pages/PageOne.ets", "buildFunction": "PageOneBuilder" }, { "name": "pageTwo", "pageSourceFile": "src/main/ets/pages/PageTwo.ets", "buildFunction": "PageTwoBuilder" } ] }

注:buildFunction是页面组件对应的@Builder函数名称。

步骤二:在 module.json5 中注册

"module": { "routerMap": "$profile:router_map" }

步骤三:主页面与跳转实现

// Index.ets (主页面) import { router } from '@kit.ArkUI'; @Entry @Component struct NavigationPage { // 创建导航路径栈 navStack: NavPathStack = new NavPathStack(); aboutToAppear() { // 将栈对象存入 AppStorage,方便子页面获取 AppStorage.setOrCreate<NavPathStack>('navStack', this.navStack); } build() { Navigation(this.navStack) { // 导航页内容(首页内容) Column({ space: 20 }) { Text('这是首页') .fontSize(30) Button('跳转到 PageOne') .onClick(() => { // 方式1:通过名称跳转,可携带参数 this.navStack.pushPath({ name: 'pageOne', param: '我是首页传来的参数' }); }) Button('跳转到 PageTwo (带回调)') .onClick(() => { // 方式2:带返回回调的跳转 this.navStack.pushPathByName('pageTwo', '参数2', (popInfo) => { console.info('PageTwo 返回了:' + JSON.stringify(popInfo.result)); }); }) } .width('100%') .height('100%') .justifyContent(FlexAlign.Center) } .mode(NavigationMode.Stack) // 设置为单栏模式 .title('主标题') .hideTitleBar(false) } }

步骤四:子页面接收参数与返回

// src/main/ets/pages/PageOne.ets // 1. Builder 函数:作为路由入口 // name 是路由名称(通常用不到),param 是传入的参数 @Builder export function PageOneBuilder(name: string, param: Object) { // 【修正点】:只传入 value,去掉不存在的 name 属性 // 注意:如果 param 可能是 undefined,建议做一下类型转换或判空 PageOne({ value: param as string }); } @Component export struct PageOne { navPathStack: NavPathStack = new NavPathStack(); // 定义接收参数的属性 @State value: string = ''; // 【新增】:在组件初始化时处理参数 aboutToAppear() { // 这里可以做一些额外的初始化逻辑 console.info('PageOne 已加载,参数为: ' + this.value); } build() { NavDestination() { Column({ space: 20 }) { Text(`接收到的参数: ${this.value}`) .fontSize(24) .fontWeight(FontWeight.Bold) Button('返回上一页') .onClick(() => { // 普通返回 this.navPathStack.pop(); }) Button('返回并带回数据') .backgroundColor('#007DFF') .fontColor(Color.White) .onClick(() => { // 带结果返回,触发上一页的回调 this.navPathStack.pop({ result: '我是PageOne带回的数据' }); }) } .width('100%') .height('100%') .justifyContent(FlexAlign.Center) } .title('PageOne') // onReady 主要用于获取 Context 和 PathStack,不建议在这里做业务数据赋值 .onReady((ctx: NavDestinationContext) => { this.navPathStack = ctx.pathStack; }) } }
// src/main/ets/pages/PageTwo.ets @Builder export function PageTwoBuilder(name: string, param: string) { PageTwo({ value: param }); } @Component export struct PageTwo { value: string = ''; @State showValue: string = ''; private navPathStack: NavPathStack = new NavPathStack(); build() { NavDestination() { Column({ space: 20 }) { Text(`PageTwo 收到: ${this.showValue}`) .fontSize(24) Button('返回并触发回调') .backgroundColor('#E65555') .onClick(() => { // 这里触发的 result 会回到 Index.ets 的 pushPathByName 回调中 this.navPathStack.pop({ result: 'PageTwo 任务完成' }); }) } .width('100%') .height('100%') .justifyContent(FlexAlign.Center) } .title('PageTwo') .onReady((ctx: NavDestinationContext) => { this.navPathStack = ctx.pathStack; this.showValue = ctx.pathInfo?.param as string || '无参数'; }) } }


二、 参数传递进阶:对象与数组的序列化陷阱

在使用NavigationRouter进行页面跳转传参时,如果传递的是复杂对象或数组,经常会遇到数据丢失或无法刷新的问题。

问题原因:
路由传参在底层会经历“序列化 + 反序列化”的过程。对于被@ObservedV2@Trace装饰的类对象,序列化后属性名会被添加__ob_前缀,导致反序列化后失去观察能力,甚至属性名错乱。

解决方案:

  1. 基础类型/简单对象:直接传递,接收时注意类型断言。
  2. 复杂对象/数组:建议使用JSON.stringify()序列化后传递,接收方再用JSON.parse()解析。

代码示例:

// Index.ets (主页面) import { router } from '@kit.ArkUI'; @Entry @Component struct NavigationPage { // 创建导航路径栈 navStack: NavPathStack = new NavPathStack(); aboutToAppear() { // 将栈对象存入 AppStorage,方便子页面获取 AppStorage.setOrCreate<NavPathStack>('navStack', this.navStack); } build() { Navigation(this.navStack) { // 导航页内容(首页内容) Column({ space: 20 }) { Text('这是首页') .fontSize(30) Button('跳转到 PageOne') .onClick(() => { // 方式1:通过名称跳转,可携带参数 this.navStack.pushPath({ name: 'pageOne', param: '我是首页传来的参数' }); }) Button('跳转到 PageTwo (带回调)') .onClick(() => { // 方式2:带返回回调的跳转 this.navStack.pushPathByName('pageTwo', '参数2', (popInfo) => { console.info('PageTwo 返回了:' + JSON.stringify(popInfo.result)); }); }) Button('跳转到文章页 (传数组)') .onClick(() => { // 发送方逻辑:准备数组并序列化 let titles: string[] = ["文章1", "文章2", "文章3"]; // 将数组序列化为字符串传递 this.navStack.pushPathByName("articlePage", JSON.stringify(titles)); }) } .width('100%') .height('100%') .justifyContent(FlexAlign.Center) } .mode(NavigationMode.Stack) // 设置为单栏模式 .title('主标题') .hideTitleBar(false) } }
// src/main/ets/pages/ArticlePage.ets // 1. 路由入口 Builder(必须加上,否则路由找不到页面) @Builder export function ArticlePageBuilder(name: string, param: Object) { // 将路由传来的参数传给组件的 initialData 属性 ArticlePage({ initialData: param as string }); } @Component export struct ArticlePage { // 接收路由传来的 JSON 字符串 initialData: string = ''; @State buttonTitles: string[] = []; private navPathStack: NavPathStack = new NavPathStack(); // 2. 在 aboutToAppear 中解析参数(比 onReady 更安全规范) aboutToAppear() { if (this.initialData) { try { this.buttonTitles = JSON.parse(this.initialData) as string[]; } catch (e) { console.error('参数解析失败:', e); } } } build() { NavDestination() { Column() { Text('接收到的文章列表:').fontSize(24).margin({ bottom: 20 }) ForEach(this.buttonTitles, (title: string) => { Button(title).margin(10) }) } .width('100%') .height('100%') .justifyContent(FlexAlign.Center) } .title('文章页面') .onReady((ctx: NavDestinationContext) => { this.navPathStack = ctx.pathStack; }) } }
{ "routerMap": [ { "name": "pageOne", "pageSourceFile": "src/main/ets/pages/PageOne.ets", "buildFunction": "PageOneBuilder" }, { "name": "pageTwo", "pageSourceFile": "src/main/ets/pages/PageTwo.ets", "buildFunction": "PageTwoBuilder" }, { "name": "articlePage", "pageSourceFile": "src/main/ets/pages/ArticlePage.ets", "buildFunction": "ArticlePageBuilder" } ] }


三、 Router 模块:传统路由方式

Router模块位于@kit.ArkUI中,适用于简单的页面跳转。虽然官方不再推荐作为首选,但在某些轻量级场景下依然有用。

1. 基础跳转

import { router } from '@kit.ArkUI'; // 跳转到指定页面 router.pushUrl({ url: 'pages/Detail', params: { id: 123 } // 传递参数 }, router.RouterMode.Standard);

2. 接收参数

import { router } from '@kit.ArkUI'; @Entry @Component struct Detail { @State id: number = 0; aboutToAppear() { // 获取路由参数 const params = router.getParams() as Record<string, number>; if (params) { this.id = params['id']; } } }

3. 页面返回

// 返回上一页 router.back(); // 返回指定页面 router.back({ url: 'pages/Index' });

四、 总结:Navigation 与 Router 对比
特性Navigation (推荐)Router (不推荐)
路由容器提供Navigation容器组件,支持标题栏、工具栏联动无容器概念,基于页面栈管理
页面栈限制无上限,支持无限跳转最大 32 层,需手动清理
转场动画支持自定义转场和共享元素动画仅支持简单自定义动画
路由拦截支持setInterception设置拦截不支持
适用场景复杂应用、多端适配、沉浸式页面简单跳转、旧项目维护

最佳实践建议:

  • 新项目:请统一使用Navigation组件,利用NavPathStack管理页面栈。
  • 参数传递:传递复杂数据时,务必使用JSON.stringifyJSON.parse进行序列化与反序列化,避免引用丢失或属性名错乱。
  • 状态管理:结合AppStorageLocalStorage可以在不同页面间共享状态,减少参数传递的复杂度。
五、 架构进阶:动态路由与模块解耦

在大型项目中,静态路由表会导致模块间强耦合。建议补充Navigation 的动态路由方案,实现业务模块的彻底解耦:

  1. 系统路由表(推荐):从 API version 12 开始,Navigation 支持在业务模块(HSP/HAR)中独立配置router_map.json。触发跳转时,系统会自动完成路由模块的动态加载与组件构建,无需主工程硬编码依赖。
  2. 自定义路由表:开发者可封装统一的路由管理模块,将NavPathStack注入其中。各业务页面通过@BuilderWrappedBuilder封装后注册到路由模块,配合动态import()实现按需加载,防止首屏加载大量代码导致卡顿。
六、 高级交互:路由拦截与沉浸式体验

Navigation 提供了 Router 无法比拟的底层控制力,可补充以下高阶 API 实战:

  1. 路由拦截(setInterception):支持在页面跳转前进行全局鉴权或状态检查。例如,在跳转核心业务页前拦截判断用户是否登录,未登录则重定向至登录页,登录成功后自动恢复原跳转。
  2. 沉浸式与自定义属性:支持通过backgroundBlurStyle设置页面背景模糊,或通过.hideTitleBar(true)隐藏默认标题栏,配合自定义CustomNavigationBar实现完全自定义的顶部导航与沉浸式全屏体验。
  3. 共享元素转场:Navigation 天然支持共享元素动画,可实现列表项到详情页的平滑过渡,大幅提升应用的视觉连贯性(Router 不支持此特性)。
七、 性能优化:参数传递与动态加载

针对原文提到的“序列化陷阱”,可补充底层原理与性能优化建议:

  1. 引用传递 vs 深拷贝:明确指出 Navigation 在传递参数时底层采用引用传递,而 Router 采用深拷贝。因此,对于超大对象或复杂数组,Navigation 不仅避免了 JSON 序列化带来的性能损耗,还能保持对象引用,配合@Observed实现跨页面的响应式更新。
  2. 组件动态加载:Router 使用@Entry修饰页面,模块加载时会生成全量页面;而 Navigation 可配合动态加载机制,仅在pushPath触发时才实例化目标组件,显著降低内存占用。
八、 状态管理与生命周期最佳实践

补充在复杂路由场景下的状态管理范式:

  1. NavPathStack 的共享范式:除了AppStorage,更推荐使用@Provide/@Consume在组件树内共享NavPathStack,这能确保路由状态与 UI 树的强绑定,避免内存泄漏并保证状态更新的实时性。
  2. 精准的生命周期监听:Navigation 提供了比 Router 更细粒度的页面生命周期。建议开发者利用onShown/onHidden处理页面可见性相关的业务逻辑(如暂停/恢复视频播放、刷新数据),利用onReady获取NavDestinationContext进行安全的参数解析与栈对象绑定。
  3. 全局路由监听:通过uiObserver.on('navDestinationUpdate')注册全局监听,可实现统一的路由埋点、页面停留时长统计等无侵入式监控。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/24 21:41:14

【claude code实践】Hooks 调试方法:让自动化流程稳定可靠

Hooks 调试方法&#xff1a;让自动化流程稳定可靠 引言&#xff1a;为什么现在需要理解它 你在使用 AI 编码助手时&#xff0c;是否遇到过这样的场景&#xff1a;它帮你自动生成了某个模块的测试代码&#xff0c;但在写入文件前&#xff0c;你并不知道这些代码能否通过 lint 检…

作者头像 李华
网站建设 2026/7/24 21:39:13

高并发内存池 - central cache 结构设计

高并发内存池 - central cache 结构设计 项目 gitee 链接&#xff1a; 高并发内存池项目 项目 github 链接&#xff1a; 高并发内存池项目 central cache 也是一个哈希桶结构&#xff0c;并且映射关系与 thread cache 保持一致&#xff0c;但是链接部分不再是自由链表&#x…

作者头像 李华
网站建设 2026/7/24 21:38:30

Cpp2IL完整指南:如何分析和理解Unity IL2CPP编译后的应用

Cpp2IL完整指南&#xff1a;如何分析和理解Unity IL2CPP编译后的应用 【免费下载链接】Cpp2IL Work-in-progress tool to reverse unitys IL2CPP toolchain. 项目地址: https://gitcode.com/gh_mirrors/cp/Cpp2IL 你是否曾经面对Unity IL2CPP编译后的GameAssembly.dll感…

作者头像 李华
网站建设 2026/7/24 21:38:20

英雄联盟智能助手Seraphine:免费开源的终极战绩查询与BP辅助工具

英雄联盟智能助手Seraphine&#xff1a;免费开源的终极战绩查询与BP辅助工具 【免费下载链接】Seraphine 英雄联盟战绩查询工具 项目地址: https://gitcode.com/gh_mirrors/se/Seraphine 你是否厌倦了在英雄联盟对局中手动查询队友战绩&#xff1f;是否希望在BP阶段就能…

作者头像 李华
网站建设 2026/7/24 21:36:11

Betaflight Configurator终极指南:5步打造完美无人机飞控系统

Betaflight Configurator终极指南&#xff1a;5步打造完美无人机飞控系统 【免费下载链接】betaflight-configurator Cross platform configuration and management application for the Betaflight firmware 项目地址: https://gitcode.com/gh_mirrors/be/betaflight-config…

作者头像 李华
网站建设 2026/7/24 21:35:14

HarmonyOS开发实战:小分享-@ohos.net.http 网络请求封装进阶

前言 网络请求封装 是大型应用的必备能力&#xff0c;包括请求拦截器、响应拦截器、错误重试、超时控制等。小分享 App 的模板列表、热门分享等数据需要完善的网络层。本篇讲解进阶网络请求封装。详细 API 可参考 HarmonyOS HTTP 官方文档。 一、请求拦截器 interface Reque…

作者头像 李华