鸿蒙应用开发实战【74】— 子组件 router 导航:import 对比 getUIContext
本文是「号码助手全栈开发系列」第 74 篇,持续更新中…
开源社区:https://openharmonycrossplatform.csdn.net
前言
在号码助手项目中,页面导航(路由跳转)有两种实现方式:import { router } from '@kit.ArkUI'的模块导入,以及this.getUIContext().getRouter()的实例获取。HomePage 等核心页面选择了 import 方式,本篇深入分析两种方式的异同、选型依据和最佳实践。
本文涵盖:router 模块导入方式详解、getUIContext().getRouter() 实例方式详解、两种方式的对比分析、HomePage 选型决策解析、子组件路由导航模式、callback 错误处理、getUIContext 的其他能力(getWindow、getDisplay 等)、路由栈管理最佳实践。
一、import { router } 方式
1.1 模块导入
这是号码助手中最常用的方式,在文件顶部通过import引入router:
// HomePage.ets — import 方式import{router}from'@kit.ArkUI';import{promptAction}from'@kit.ArkUI';@Entry@Componentstruct HomePage{build(){Column(){// 页面内容...}}/** * 统一页面导航方法 */privatenavigate(url:string,params?:Record<string,Object>):void{router.pushUrl({url:`pages/${url}`,params:params??{},}).then(()=>{hilog.info(0x0000,'HomePage','navigate success: %{public}s',url);}).catch((error:Error)=>{hilog.error(0x0000,'HomePage','navigate failed: %{public}s',error.message);promptAction.showToast({message:`页面跳转失败:${error.message}`,});});}}1.2 FAB 按钮导航示例
// HomePage.ets — FAB 悬浮按钮导航build(){Stack(){// 主内容区域this.MainContent()// FAB 悬浮按钮Image($r('app.media.ic_add')).width(56).height(56).position({bottom:24,right:24}).onClick(()=>{this.showActionSheet();})}.width('100%').height('100%')}privateshowActionSheet():void{ActionSheet.show({title:'选择操作',buttons:[{title:'添加卡号',action:()=>this.navigate('AddCardPage'),},{title:'批量导入',action:()=>this.navigate('PasteImportPage'),},{title:'取消',action:()=>{},},],});}1.3 带参数导航
// HomePage.ets — 统计卡片点击跳转privateonStatCardClick(status:string):void{this.navigate('StatusListPage',{status});}// 应用列表行点击privateonAppItemClick(app:AppBinding):void{this.navigate('AppDetailPage',{appId:app.id});}二、this.getUIContext().getRouter() 方式
2.1 组件实例获取
在自定义组件内部,通过getUIContext()获取路由实例:
// 子组件中通过 getUIContext 导航@Componentstruct AppListItem{@Propapp:AppBinding|null=null;build(){Row(){// 应用信息展示Text(this.app?.app_name??'')Text(this.app?.status??'')}.onClick(()=>{this.navigateToDetail();})}privatenavigateToDetail():void{constrouter=this.getUIContext().getRouter();router.pushUrl({url:'pages/AppDetailPage',params:{appId:this.app?.id??0,},}).catch((error:Error)=>{console.error('navigate failed:',error.message);});}}2.2 在 @Builder 中使用
在@Builder函数中也可以使用getUIContext():
@Componentstruct CardManagePage{@BuilderCardItem(card:CardEntity){Column(){Text(card.label).fontSize(16).fontWeight(FontWeight.Bold)Text(this.formatPhone(card.phone_number)).fontSize(13).fontColor('#68708A')}.onClick(()=>{// 在 @Builder 中需要使用 getUIContextthis.getUIContext().getRouter().pushUrl({url:'pages/CardDetailPage',params:{cardId:card.id},}).catch((error:Error)=>{promptAction.showToast({message:'跳转失败'});});})}}三、两种方式深度对比
3.1 核心特性对比
| 特性 | import { router } | getUIContext().getRouter() |
|---|---|---|
| 导入方式 | 模块静态导入 | 实例方法获取 |
| 生命周期依赖 | 无 | 需要组件已挂载 |
| 编译时检查 | 有(静态类型检查) | 无(运行时获取) |
| 树摇优化 | 支持 | 不支持 |
| 上下文绑定 | 全局单例 | 组件树上下文 |
| 在 @Builder 中使用 | 直接使用 | 需this.getUIContext() |
| 在非组件中使用 | 可以用 | 不可以用 |
| 在回调中使用 | 闭包捕获即可 | 可能丢失this上下文 |
3.2 为什么 HomePage 使用 import 方式
HomePage 选择import { router }的核心原因:
| 因素 | import 方式 | getUIContext 方式 |
|---|---|---|
| 调用频率 | 高频(多个导航点) | 同等可用 |
| 代码简洁度 | 一行router.pushUrl | this.getUIContext().getRouter().pushUrl |
| 回调上下文 | 无this丢失问题 | 需注意this指向 |
| 统一封装 | 容易封装为全局工具NavigationUtil | 只能在组件内使用 |
| 静态分析 | IDE 可提供导入提示 | 无法静态分析 |
// 结论:HomePage 使用 import 封装为统一方法classNavigationUtil{staticpush(url:string,params?:Record<string,Object>):Promise<void>{returnrouter.pushUrl({url:`pages/${url}`,params:params??{}});}}// 所有页面统一调用NavigationUtil.push('StatusListPage',{status:'待换绑'});3.3 何时使用 getUIContext 方式
// 场景一:自定义子组件中需要导航@Componentexportstruct MyCustomButton{privatetargetUrl:string='';build(){Button('跳转').onClick(()=>{// 子组件中推荐使用 getUIContextthis.getUIContext().getRouter().pushUrl({url:this.targetUrl,});})}}// 场景二:@Builder 中需要导航@BuilderfunctionGlobalActionButton(icon:Resource,url:string){Image(icon).width(48).height(48).onClick(()=>{// @Builder 函数中 this 不可用,需通过参数传递})}四、getUIContext 的其他能力
4.1 获取窗口信息
getUIContext()不仅能获取 Router,还能获取窗口、显示等信息:
@Componentstruct CustomComponent{build(){Column(){// ...}}privategetWindowSize():void{constwindow=this.getUIContext().getWindow();// 获取窗口属性constproperties=window.getWindowProperties();hilog.info(0x0000,'Component','window size: %{public}d x %{public}d',properties.windowRect.width,properties.windowRect.height);}privategetDisplayInfo():void{constdisplay=this.getUIContext().getDisplay();hilog.info(0x0000,'Component','display dpi: %{public}d',display.densityDPI);}}4.2 getUIContext 可用方法
| 方法 | 返回类型 | 用途 |
|---|---|---|
getRouter() | Router | 页面路由导航 |
getWindow() | Window | 窗口属性与操作 |
getDisplay() | Display | 屏幕显示信息 |
getFont() | Font | 字体管理 |
getMediaQuery() | MediaQuery | 媒体查询 |
getFolder() | Folder | 文件夹操作 |
五、路由方法详解
5.1 常用路由 API
| 方法 | 说明 | 参数 |
|---|---|---|
pushUrl(options) | 压入新页面 | { url, params } |
replaceUrl(options) | 替换当前页面 | { url, params } |
back(options?) | 返回上一页 | { url, params? } |
getParams() | 获取当前页面参数 | 无 |
getState() | 获取路由栈状态 | 无 |
clear() | 清空路由栈 | 无 |
5.2 路由栈管理
// 获取路由栈状态privatecheckRouteStack():void{conststate=router.getState();hilog.info(0x0000,'Router','route stack: index=%{public}d, size=%{public}d',state.index,state.size);}// 返回并传参privategoBackWithResult():void{router.back({url:'pages/HomePage',params:{result:'success'},});}// 替换页面(不保留返回)privatereplaceToLogin():void{router.replaceUrl({url:'pages/LoginPage',});}六、错误处理与最佳实践
6.1 统一错误处理
// common/utils/RouteUtil.ets — 统一路由管理import{router}from'@kit.ArkUI';import{promptAction}from'@kit.ArkUI';import{hilog}from'@kit.PerformanceAnalysisKit';constTAG='RouteUtil';exportclassRouteUtil{staticpush(url:string,params?:Record<string,Object>):Promise<void>{returnrouter.pushUrl({url:`pages/${url}`,params:params??{},}).catch((error:Error)=>{hilog.error(0x0000,TAG,'pushUrl failed: url=%{public}s, error=%{public}s',url,error.message);promptAction.showToast({message:'页面跳转失败,请重试',duration:2000,});});}staticreplace(url:string,params?:Record<string,Object>):Promise<void>{returnrouter.replaceUrl({url:`pages/${url}`,params:params??{},}).catch((error:Error)=>{hilog.error(0x0000,TAG,'replaceUrl failed: %{public}s',error.message);});}staticback(params?:Record<string,Object>):void{router.back({params:params??{}});}}6.2 子组件导航模式
// 模式一:回调给父组件(推荐)@Componentstruct AppListItem{@Propapp:AppBinding|null=null;privateonItemClick?:(app:AppBinding)=>void;build(){Row(){Text(this.app?.app_name??'')}.onClick(()=>{if(this.onItemClick&&this.app){this.onItemClick(this.app);}})}}// 父组件处理导航@Entry@Componentstruct AppListPage{build(){ForEach(this.apps,(app:AppBinding)=>{AppListItem({app:app,onItemClick:(item:AppBinding)=>{RouteUtil.push('AppDetailPage',{appId:item.id});},})})}}// 模式二:子组件直接导航(简单场景)@Componentstruct SimpleLink{privateurl:string='';privateparams?:Record<string,Object>;build(){Text('查看详情').fontColor('#4F7CFF').onClick(()=>{this.getUIContext().getRouter().pushUrl({url:this.url,params:this.params??{},});})}}小结
| 要点 | import router | getUIContext.getRouter | 推荐场景 |
|---|---|---|---|
| 模块导入 | 静态 import | 实例方法获取 | 工具类 |
| 组件内导航 | 直接使用 | this.getUIContext() | 子组件 |
| @Builder 中 | 直接使用 | 需注意 this 上下文 | @Builder |
| 统一封装 | 容易封装 | 不方便封装 | 项目全局 |
| 静态分析 | 支持 | 不支持 | 开发体验 |
| HomePage 选型 | ✅ 采用 | 可选 | 高频导航页面 |
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源:
- openHarmony 跨平台社区:https://openharmonycrossplatform.csdn.net
- HarmonyOS UI 上下文 API 文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-uicontext-0000001821000749
- HarmonyOS 路由 API 官方文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-references/router-0000001820880781