当前位置: 首页 > news >正文

《鸿蒙原生应用开发实战》第四篇:多页面导航与参数传递实战

《鸿蒙原生应用开发实战》第四篇:多页面导航与参数传递实战

前言

一个真正的应用不可能只有一个页面。页面之间的跳转、参数传递、数据回传是开发中频率最高的操作。HarmonyOS 提供了强大的router路由模块来管理页面导航。

在本篇中,我们将结合「光遇·心境」应用的 5 个页面,深入讲解:

  • router 路由核心 API
  • 页面间参数传递的多种方式
  • 页面生命周期与数据加载时机
  • 路由栈管理与返回处理
  • 参数类型安全与错误处理

一、路由系统概述

路由表配置

所有页面必须在main_pages.json中注册:

{"src":["pages/Index","pages/ScenePage","pages/DetailPage","pages/FavPage","pages/ProfilePage"]}

注册时只写路径(不含.ets后缀),路径相对于ets/目录。5 个页面组成了一个完整的导航体系:

┌─────────────┐ │ Index │ ← 首页(入口) └──────┬──────┘ ┌────────────────┼────────────────┐ ▼ ▼ ▼ ┌──────────┐ ┌───────────┐ ┌───────────┐ │ ScenePage│ │ FavPage │ │ProfilePage│ └─────┬────┘ └───────────┘ └───────────┘ ▼ ┌───────────┐ │DetailPage │ ← 详情页(可来自多个入口) └───────────┘

二、router 核心 API

导入方式

API 23 强制要求从@ohos.router导入:

// ✅ 正确 — API 23 支持的方式importrouterfrom'@ohos.router';// ❌ 错误 — API 23 不导出此路径// import { router } from '@kit.AbilityKit';

页面跳转

// 基本跳转(无参数)router.pushUrl({url:'pages/FavPage'});// 带参数跳转router.pushUrl({url:'pages/DetailPage',params:{sceneId:1}});// 多个参数router.pushUrl({url:'pages/ScenePage',params:{category:'海洋',from:'homepage'}});

页面返回

// 简单返回上一页router.back();// 返回并携带数据回传router.back({url:'pages/Index',params:{result:'fromDetail'}});

获取参数

// 在目标页面的 aboutToAppear 中获取参数aboutToAppear():void{constparams=router.getParams()asRecord<string,Object>;if(params&&params['sceneId']){constid=params['sceneId']asnumber;this.scene=getSceneById(id);}}

三、参数传递实战案例

案例1:首页 → 场景列表(分类筛选)

首页的分类入口点击后,将分类名称传递给 ScenePage:

// Index.ets — 分类入口点击CategoryItem(name:string){Column(){Text(this.categoryIcons[name]||'✨').fontSize(28)Text(name).fontSize($r('app.float.caption_font_size'))}.onClick(()=>{router.pushUrl({url:'pages/ScenePage',params:{category:name}// 传递"晨光"/"森林"等});})}

在 ScenePage 接收并使用参数:

// ScenePage.ets@StateselectedCategory:string='全部';@Statescenes:SceneItem[]=getScenes();aboutToAppear():void{constparams=router.getParams()asRecord<string,Object>;if(params&&params['category']){constcat=params['category']asstring;this.selectedCategory=cat;this.scenes=getScenesByCategory(cat);// 根据分类筛选}}

效果:从首页点击"海洋"分类 → 直接跳转到 ScenePage 并展示所有海洋场景。

案例2:多个入口 → 详情页

DetailPage 可以从 3 个不同入口进入:

// 入口1:首页的每日推荐卡片// Index.etsrouter.pushUrl({url:'pages/DetailPage',params:{sceneId:this.dailyScene.id}});// 入口2:场景列表的卡片// ScenePage.etsrouter.pushUrl({url:'pages/DetailPage',params:{sceneId:item.id}});// 入口3:收藏列表的卡片// FavPage.etsrouter.pushUrl({url:'pages/DetailPage',params:{sceneId:item.id}});

详情页统一接收:

// DetailPage.ets@Statescene:SceneItem|undefined=undefined;aboutToAppear():void{constparams=router.getParams()asRecord<string,Object>;if(params&&params['sceneId']){constid=params['sceneId']asnumber;this.scene=getSceneById(id);if(this.scene){this.checkFavStatus();}}}

设计原则:DetailPage 不关心从哪个入口来的,它只读取sceneId参数。这种松耦合的设计让代码更容易维护。


四、页面生命周期与数据加载

页面生命周期方法

方法触发时机适用场景
aboutToAppear()页面即将显示(每次进入)加载数据、读取参数
onPageShow()页面每次显示时刷新数据(从其他页面返回时)
onPageHide()页面隐藏时暂停动画/音视频
aboutToDisappear()页面销毁前释放资源

aboutToAppear vs onPageShow 的区别

// 场景:收藏页 → 详情页 → 点击收藏 → 返回收藏页// aboutToAppear() — 只在新创建页面时调用// 第一次进入 FavPage: ✅ 调用// 从 DetailPage 返回 FavPage: ❌ 不调用(页面还在栈中)// onPageShow() — 每次页面显示时都调用// 第一次进入 FavPage: ✅ 调用// 从 DetailPage 返回 FavPage: ✅ 调用

这就是为什么我们在 FavPage 中使用aboutToAppear而不是onPageShow—— 因为收藏列表在返回时需要自动刷新,我们需要在每次显示时重新加载数据:

@Componentstruct FavPage{@StatefavScenes:SceneItem[]=[];aboutToAppear():void{this.loadFavScenes();// 每次进入页面时刷新收藏列表}loadFavScenes():void{constfavIds:number[]=AppStorage.get<number[]>(FAV_KEY)||[];constlist:SceneItem[]=[];for(constidoffavIds){constscene=getSceneById(id);if(scene){list.push(scene);}}this.favScenes=list;this.favCount=list.length;}}

性能建议

  • aboutToAppear中不要做耗时操作(如网络请求),如果需要异步加载数据,使用aboutToAppear发起请求,用@State监听结果
  • 频繁刷新的数据用onPageShow,一次性初始化用aboutToAppear

五、路由栈管理

路由栈的工作机制

router 内部维护了一个页面栈(LIFO):

初始状态: [Index] push FavPage: [Index, FavPage] push DetailPage: [Index, FavPage, DetailPage] back(): [Index, FavPage] ← DetailPage 出栈 back(): [Index] ← FavPage 出栈

router.pushUrl 与 router.replaceUrl

API行为路由栈
pushUrl压入新页面,保留当前页面栈增长
replaceUrl替换当前页面,不保留栈不变
back()弹出栈顶页面栈缩小
clear()清空栈空栈

replaceUrl的典型使用场景是登录页 → 首页:

// 登录成功后替换,用户无法返回到登录页router.replaceUrl({url:'pages/Index'});

避免路由栈过深

理论上路由栈可以无限增长,但过深的栈会消耗内存。建议:

  • 不用的页面及时back()
  • 使用replaceUrl替代某些场景的pushUrl
  • 监听页面栈数量

六、参数传递的高级话题

参数类型安全

由于router.getParams()返回的是Object类型,需要进行类型断言:

// 安全获取参数的模式aboutToAppear():void{constparams=router.getParams()asRecord<string,Object>|undefined;// 使用可选链和类型守卫constsceneId=params?.['sceneId'];if(typeofsceneId==='number'){this.scene=getSceneById(sceneId);}// 或者使用断言 + 兜底constcat=(params?.['category']asstring)||'全部';this.selectedCategory=cat;}

可以传递的数据类型

类型示例是否支持
string{ name: 'hello' }
number{ id: 42 }
boolean{ isAdmin: true }
Object{ user: { name: 'Tom' } }
Array{ ids: [1,2,3] }
Function{ cb: ()=>{} }❌ 不支持
复杂嵌套对象含多层嵌套✅ 会被序列化

参数的大小限制

路由参数不要传大数据(超过 10KB 的数据建议用 AppStorage 或全局变量共享),因为参数在序列化/反序列化过程中有性能开销。


七、完整的导航流程图

┌─────────────────────────────────────────────────────────────┐ │ 用户操作流程 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 打开App → Index(首页) │ │ ├── 点击"每日推荐" → push({sceneId}) → DetailPage │ │ │ └── 点击收藏 ❤️ → AppStorage 更新 → back │ │ │ │ │ ├── 点击"晨光"分类 → push({category: '晨光'}) → ScenePage│ │ │ └── 点击场景卡片 → push({sceneId}) → DetailPage │ │ │ └── back → back │ │ │ │ │ ├── 点击❤️按钮 → push({}) → FavPage │ │ │ ├── 点击卡片 → push({sceneId}) → DetailPage │ │ │ └── 点击取消收藏 → AppStorage 更新 │ │ │ │ │ └── 点击👤按钮 → push({}) → ProfilePage │ │ └── back │ │ │ │ 任意页面 ← back 返回上一页 │ │ │ └─────────────────────────────────────────────────────────────┘

八、常见踩坑记录

坑1:使用 @kit.AbilityKit 的 router

现象:编译通过但运行时 crash
原因:API 23 不导出@kit.AbilityKit中的 router
解决:统一使用import router from '@ohos.router'

坑2:参数接收不到

现象router.getParams()返回 undefined
原因:在aboutToAppear之前调用,或页面未通过 router 进入
解决:在aboutToAppear生命周期中获取参数

坑3:返回时数据没有刷新

现象:从详情页取消收藏返回收藏页,列表没有变化
原因:FavPage 在aboutToAppear中加载数据,但页面在栈中未被销毁,不会调用aboutToAppear
解决:使用onPageShow()代替,或在 AppStorage 上注册监听


九、路由与状态管理的协作模式

// 推荐的协作模式// 1. 路由负责页面导航和轻量参数router.pushUrl({url:'pages/DetailPage',params:{sceneId:1}// 只传递 ID,不传递完整数据});// 2. 共享数据通过 AppStorage 管理AppStorage.set<number[]>(FAV_KEY,favList);// 3. 页面从数据仓库获取完整数据constscene=getSceneById(sceneId);// 通过 ID 获取

这种设计的好处:

  • 参数简洁,只传 ID 不传整个对象
  • 数据源统一,避免多页面数据不一致
  • 调试方便,数据流向清晰

总结

本篇我们深入学习了:

  1. ✅ router 路由核心 API 与导入规范
  2. ✅ 4 种参数传递实战场景
  3. ✅ 页面生命周期与数据加载时机
  4. ✅ 路由栈管理与返回机制
  5. ✅ 参数类型安全与错误处理
  6. ✅ 路由与状态管理的协作模式

路由是连接页面的桥梁,掌握好这一章,多页面应用开发就会得心应手。最后一篇将聚焦完整的收藏功能闭环与应用发布准备。

下一篇预告:收藏功能、资源管理与构建发布 —— 从功能闭环到 HAP 包发布全流程

http://www.zskr.cn/news/1524926.html

相关文章:

  • [苹果催审核]
  • 合肥市肥东县 水电维修|维小达|电路维修、水管维修、管道疏通、马桶暖气维修、防水补漏一站式维保服务 - 维小达科技
  • 2026年6月成都黄金回收口碑优质榜:十家门店经过七项筛选三大考核 仅剩下三家选择 - 天天生活分享日志
  • 如何用BiliRaffle快速完成B站动态抽奖:面向UP主的完整指南
  • 2026 北京梵克雅宝珠宝别乱卖 线上线下比对选诚信正规回收商 - 讯息早知道
  • 2026天津钻石回收实测指南|添价收市内六区+环城四区全域极速上门 - 薛定谔的梨花猫
  • 2026年上海黄金变现回收口碑红榜:四家老店深度实测丨资质证书怎么看+交易技巧 - 天天生活分享日志
  • 2026 南京 K 金铂金首饰回收报价实测,五大靠谱门店行情白皮书 - 讯息早知道
  • LinkSwift:一键获取九大网盘直链的高效下载解决方案
  • Windows Node.js版本管理技术方案如何解决多环境开发痛点:企业级nvm-windows架构实践指南
  • MPC8313E总线仲裁与监控机制:嵌入式多主设备系统性能与稳定性的核心
  • 2026最新推荐 很多老师在用的适合学生练词汇的英语单词APP
  • 2026年衡阳市CPPM考试最新全攻略:科目题型、通过率、备考重点及官方双认证报考机构推荐 - 众智商学院课程中心
  • [智能体-416]:Coze平台开发的智能体应用,发布到第三方平台的载体是什么?最终的代码是运行第三发平台,如手机端,还是最终运行在Coze平台上,只不过是提供的远程服务?
  • 滑动窗口异常检测方法识别异常数据点
  • Deceive终极指南:三步实现游戏隐身,享受专属游戏时光
  • 终极指南:3步掌握Switch文件解析神器hactool
  • 2026年莆田市PMP培训机构哪家好?官方授权R.E.P.报考指南 - 众智商学院课程中心
  • 影刀RPA新手教程_条件判断与分支逻辑从入门到工程级实战
  • 2026 宁波天然 A 货翡翠全面回收,手镯吊坠摆件等藏品都可预约上门估价 - 薛定谔的梨花猫
  • 终极AutoHotkey v2转换指南:如何快速完成v1脚本升级的完整方法
  • PowerQUICC II SMC与MCC控制器深度解析:从GCI协议到多通道HDLC实战
  • WSL2深度学习环境配置:用CUDA 11.8和软链接搞定多项目版本隔离
  • 3分钟免费解锁Cursor AI编程助手:终极破解工具使用指南
  • 宇树GO2机器人ROS2 SDK:3小时快速实现智能四足机器人自主导航的完整指南
  • 如何快速美化foobar2000:面向新手的完整指南
  • MPC185硬件加密协处理器寄存器配置详解:DEU、AFEU、MDEU核心单元操作指南
  • MPC8260 IMA编程实战:IDCR接收控制与APC动态带宽管理详解
  • 解决实时面部交换的技术挑战:Deep-Live-Cam的AI驱动架构与性能优化方案
  • ARM9嵌入式系统设计:AHB总线时序与中断控制器AITC深度解析