鸿蒙实战:路由参数传递与接收
鸿蒙实战:路由参数传递与接收
前言(必读)
图:鸿蒙实战:路由参数传递与接收(第 74 篇) 运行效果截图(HarmonyOS NEXT)
页面跳转时携带参数是导航系统的核心能力——「从档案列表点开某一条报告」「从拍照页跳转到分析页并带上图片路径」。HarmonyOS 的routerAPI 提供了简洁的pushUrl+params参数传递方案。本文从项目中的全部路由调用入手,详解参数传递的设计模式。
图:router.pushUrl 参数传递与 getParams 接收的完整流程示意
一、路由参数 API
1.1 发送端:pushUrl + params
// 带参数跳转this.getUIContext().getRouter().pushUrl({url:'pages/ReportDetailPage',params:{archiveId:item.id,source:'archive'// 可传任意 key-value}})// 无参数跳转(纯导航)this.getUIContext().getRouter().pushUrl({url:'pages/CapturePage'})// 替换当前页(不保留历史栈)this.getUIContext().getRouter().replaceUrl({url:'pages/HomePage'})1.2 接收端:getParams
// 接收参数aboutToAppear(){constparams=this.getUIContext().getRouter().getParams()if(params&&typeofparams==='object'){constp=paramsasRecord<string,Object>constarchiveId=p['archiveId']if(typeofarchiveId==='number'){this.currentArchiveId=archiveId}}}1.3 API 对照
| 方法 | 行为 | 历史栈 |
|---|---|---|
pushUrl({ url, params }) | 入栈跳转 | 保留当前页 |
replaceUrl({ url, params }) | 替换当前页 | 不保留 |
back() | 返回上一页 | 出栈 |
getParams() | 获取当前页的参数 | — |
二、项目路由调用全景
2.1 路由矩阵
| 跳转(源 → 目标) | 方法 | 参数 | 用途 |
|---|---|---|---|
| LoginPage → HomePage | replaceUrl | 无 | 登录成功后替换 |
| HomePage → CapturePage | pushUrl | 无 | 拍照 |
| HomePage → ArchivePage | pushUrl/replaceUrl | 无 | 查看档案列表 |
| HomePage → ReportDetailPage | pushUrl | { reportId } | 查看报告详情 |
| HomePage → CoupleHomePage | replaceUrl | 无 | 跳转合盘首页 |
| HomePage → TrendPage | replaceUrl | 无 | 跳转情绪趋势 |
| HomePage → MePage | replaceUrl | 无 | 跳转个人中心 |
| ArchivePage → ReportDetailPage | pushUrl | { archiveId } | 从档案查看报告 |
| UnclassifiedArchivePage → ReportDetailPage | pushUrl | { handwritingId } | 从未分类查看报告 |
| ReportDetailPage → SharePage | pushUrl | 无 | 分享报告 |
| ReportDetailPage → DuetReportPage | pushUrl | 无 | 查看合盘 |
| MePage → CoupleHomePage | pushUrl | 无 | 查看合盘 |
| MePage → BindRelationPage | pushUrl | 无 | 绑定关系 |
| AnalyzingPage → ReportDetailPage | pushUrl | 无 | 分析完成跳转 |
2.2 Tab 导航:replaceUrl
所有 Tab 页面之间使用replaceUrl,避免历史栈无限增长:
// Tab 导航统一模式.onClick(()=>{if(index===0){this.getUIContext().getRouter().replaceUrl({url:'pages/HomePage'})}elseif(index===1){this.getUIContext().getRouter().replaceUrl({url:'pages/ArchivePage'})}elseif(index===2){this.getUIContext().getRouter().replaceUrl({url:'pages/TrendPage'})}elseif(index===3){this.getUIContext().getRouter().replaceUrl({url:'pages/MePage'})}})三、典型参数传递场景
3.1 archiveId / reportId 双通道
// ArchivePage → ReportDetailPage.onClick(()=>{AppStorage.setOrCreate<number>('current_archive_id',item.id)this.getUIContext().getRouter().pushUrl({url:'pages/ReportDetailPage',params:{archiveId:item.id}})})// ReportDetailPage — 接收端aboutToAppear(){letarchiveId:number|undefinedletreportId:number|undefined// 通道 1:从 router params 读取constparams=this.getUIContext().getRouter().getParams()if(params&&typeofparams==='object'){constp=paramsasRecord<string,Object>constpArchiveId=p['archiveId']if(typeofpArchiveId==='number')archiveId=pArchiveIdconstpReportId=p['reportId']if(typeofpReportId==='number')reportId=pReportId}// 通道 2:从 AppStorage 兜底if(!reportId){reportId=AppStorage.get<number>('current_report_id')??0}// 从 archiveId 加载报告列表if(archiveId){this.loadReportsByArchive(archiveId)}elseif(reportId){this.loadReportById(reportId)}}架构设计意图:双通道(router params + AppStorage)参数传递,确保不同来源(档案列表 / 首页最近报告 / 分析页自动跳转)都能正确加载。
3.2 handwritingId 传递(未分类)
// UnclassifiedArchivePage.etsprivateviewDetail(item:UnclassifiedItem):void{this.getUIContext().getRouter().pushUrl({url:'pages/ReportDetailPage',params:{handwritingId:item.id}})}3.3 MetaServiceCard 元服务参数
// MetaServiceCard.ets — 元服务卡片从外部参数读取aboutToAppear(){constparams=this.getUIContext().getRouter().getParams()if(params&&typeofparams==='object'){constp=paramsasRecord<string,string>if(p['personality'])this.personalityType=p['personality']if(p['name'])this.userName=p['name']if(p['time'])this.updateTime=p['time']}}四、参数类型安全
4.1 类型问题
getParams()返回类型为Object | string | undefined,需要进行类型转换:
// 安全的参数读取模式functionparseRouterParams(params:Object|string|undefined):Record<string,string>{if(!params)return{}if(typeofparams==='string'){// 当参数是字符串时的处理(可能是 URL 编码的参数)try{returnJSON.parse(params)}catch{return{}}}// 对象类型 → 转为 RecordreturnparamsasRecord<string,string>}4.2 类型断言
aboutToAppear(){constparams=this.getUIContext().getRouter().getParams()// 类型守卫if(params&&typeofparams==='object'){constp=paramsasRecord<string,Object>// 每个字段独立类型检查constarchiveId=p['archiveId']if(typeofarchiveId==='number'){this.currentArchiveId=archiveId// 确保是 number}constsource=p['source']if(typeofsource==='string'){this.sourcePage=source}}}五、replaceUrl 与 pushUrl 的选择策略
// ⚠️ 使用 replaceUrl 的场景:// 1. Tab 切换(避免历史栈膨胀到 10+ 页面)// 2. 登录页 → 首页(用户不应返回登录页)// 3. 分析页 → 报告详情(分析页已完成使命)// ⚠️ 使用 pushUrl 的场景:// 1. 列表 → 详情(用户可以返回列表)// 2. 详情 → 分享(用户可以返回详情)// 3. 逐级深入(A → B → C,用户期望逐级回退)六、路由参数生命周期
pushUrl({ url: 'PageB', params: {...} }) │ ├── PageB.aboutToAppear() ← 此时可以 getParams() │ ├── PageB.build() ← UI 渲染 │ ├── 用户操作... │ ├── PageB.back() ← 返回 │ └── params 自动销毁(不再可访问)参数生命周期规则:
| 事件 | 参数状态 |
|---|---|
pushUrl调用后 | 目标页面可用 |
目标页面aboutToAppear | ✅ 可读取 |
目标页面build | ✅ 可读取 |
目标页面back() | 参数自动释放 |
| 页面被覆盖(新 push) | 参数保留(历史栈中) |
七、HarmonyOS 路由 vs 其他框架
| 框架 | 路由 API | 参数传递 |
|---|---|---|
| ArkUI (router) | pushUrl({ url, params }) | getParams()对象/字符串 |
| ArkUI (Navigation) | NavPathStack.pushPath({ name, param }) | NavPathStack.getParamByName() |
| Flutter | Navigator.pushNamed(context, route, args) | ModalRoute.of(context)!.settings.arguments |
| React Native | navigation.navigate('Route', { params }) | route.params |
| Vue Router | router.push({ path, query }) | $route.query |
Navigationvsrouter:Navigation 组件(API 12+)提供了更高级的路由能力,但本项目基于早期 SDK 开发,使用router已完全满足功能需求。
八、注意事项与常见问题
8.1 开发注意事项
在正式开发前,建议按以下步骤完成环境准备与前置检查:
- 版本确认:检查 DevEco Studio 与 SDK 版本,确保满足目标 API Level 要求
- 权限声明:在
module.json5的requestPermissions字段中提前声明所有需要的系统权限 - 设备能力检查:调用前验证设备是否支持目标能力(相机、NFC、传感器等)
- 异步封装:所有耗时操作(数据库、文件 I/O、网络请求)统一使用
async/await处理 - 资源释放:在组件
aboutToDisappear()生命周期钩子中及时释放系统资源,防止内存泄漏
8.2 常见错误与解决方案
常见问题快速排查表:
| 问题类型 | 排查方向 | 参考方法 |
|---|---|---|
| 应用崩溃 | 查看 hilog 错误日志 | hilog.error(TAG, "...", e.message) |
| 状态丢失 | 检查 AppStorage 键名拼写 | 统一使用常量管理键名 |
| 动画不流畅 | 避免在 animateTo 回调中执行 I/O | 动画与数据操作分离 |
总结
- ✅pushUrl/replaceUrl:入栈与替换两种路由模式
- ✅参数传递:params 对象支持任意类型的 key-value
- ✅参数接收:
getParams()+ 类型守卫安全读取 - ✅双通道架构:router params + AppStorage 双重兜底
- ✅路由全景:15+ 路由调用覆盖全部页面跳转
- ✅类型安全:Record 断言 + 字段级类型检查
- ✅路由选择策略:Tab 用 replaceUrl,深入用 pushUrl
下一篇将讲解getUIContext 与上下文获取。
📌 收藏提示:路由参数传 ID 不传对象——确保参数轻量且页面可独立加载。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源:
- Navigation 路由
- NavPathStack 路由栈
- router 页面跳转
- 第05篇 路由配置
- 第72篇 AppStorage
- 第73篇 @State
- 第99篇 设计模式回顾
- 页面间通信最佳实践
七、路由参数的高级模式
7.1 类型安全的参数接收
推荐使用接口类型声明统一管理每个页面的参数结构:
// 集中定义所有路由参数类型interfaceReportDetailParams{reportId:number;archiveId?:number;fromPage?:string;// 用于回退时的导航决策}interfaceAnalyzingParams{imagePath:string;archiveId:number;captureTime:number;}// 接收时使用类型断言constparams=router.getParams()asReportDetailParams;if(params?.reportId){this.loadReport(params.reportId);}7.2 参数的持久化兜底
路由参数只在内存中,页面旋转/后台切换后可能丢失,建议配合 AppStorage 做兜底:
onPageShow(){constparams=router.getParams()asReportDetailParams;if(params?.reportId){AppStorage.set('lastReportId',params.reportId);this.loadReport(params.reportId);}else{// 兜底:读取 AppStorage 中的上次 IDconstlastId=AppStorage.get<number>('lastReportId');if(lastId)this.loadReport(lastId);}}7.3 路由参数 vs AppStorage 的选择
| 数据特征 | 推荐方式 |
|---|---|
| 单次跳转携带的 ID | router params |
| 需要跨多个页面访问 | AppStorage |
| 从子页面返回给父页面 | EventHub 事件总线 |
| 大型对象(图片等) | AppStorage 或文件路径 |
核心原则:路由参数是「一次性邮件」,AppStorage 是「公告栏」,EventHub 是「内部电话」——根据数据的传播方向和生命周期选择合适方式。
7.4 Navigation 路由的参数传递(未来演进方向)
随着 HarmonyOS 对Navigation组件的推进,未来可能逐步替代routerAPI:
// Navigation 方式(路由栈模式)pathStack.pushPath({name:'ReportDetail',param:{reportId:1}});// 目标页接收@ConsumepathStack:NavPathStack;constparams=this.pathStack.getParamByName('ReportDetail')[0]asReportDetailParams;如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
