23 组件生命周期:aboutToAppear、aboutToDisappear、onPageShow
23 组件生命周期:aboutToAppear、aboutToDisappear、onPageShow
前言
图:23 组件生命周期:aboutToAppear、aboutToDisappear、onPageShow 运行效果截图(HarmonyOS NEXT)
每个 ArkUI 组件从创建到销毁都有一个明确的生命周期。了解生命周期方法的调用时机,可以帮助开发者在正确的时机做正确的事——在创建时加载数据、在显示时刷新界面、在销毁时释放资源。
本文以"鹿鹿·笔迹心理分析"项目中多个页面的生命周期实践为例,深入解析 ArkUI 组件生命周期的调用顺序、使用场景和最佳实践。
鸿蒙官方·组件生命周期:developer.huawei.com
项目源码仓库:harmony-app GitHub
图:ArkUI 组件从创建到销毁的完整生命周期时序——包括 @Entry 专属的 onPageShow/onPageHide
一、ArkUI 组件生命周期总览
1.1 生命周期方法
ArkUI 组件(@Component)提供了以下生命周期方法:
| 方法 | 调用时机 | 用途 | 是否所有组件都有 |
|---|---|---|---|
aboutToAppear | 组件创建后、build() 前 | 初始化数据、触发入场动画 | ✅ 所有组件 |
build() | 组件首次渲染 | 构建 UI 描述 | ✅ 必须实现 |
aboutToDisappear | 组件销毁前 | 清理定时器、释放资源 | ✅ 所有组件 |
onPageShow | 页面显示时(每次) | 刷新页面数据 | ❌ 仅 @Entry 组件 |
onPageHide | 页面隐藏时 | 暂停操作 | ❌ 仅 @Entry 组件 |
1.2 生命周期时序
一个典型页面的生命周期调用顺序:
页面路由跳转 ↓ aboutToAppear ← ① 组件创建,初始化数据 ↓ build() ← ② 首次构建 UI ↓ onPageShow ← ③ 页面可见(每次返回页面都会触发) ↓ (用户使用页面) ↓ 路由跳转/返回 ↓ onPageHide ← ④ 页面不可见 ↓ aboutToDisappear ← ⑤ 组件销毁,清理资源关键区别:
aboutToAppear在组件首次创建时调用一次;onPageShow在页面每次显示时调用(包括从其他页面返回时)。
二、aboutToAppear:组件初始化
2.1 数据加载
[ReportDetailPage.ets](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/pages/ReportDetailPage.ets) 在aboutToAppear中完成数据初始化:
aboutToAppear(){// ① 初始化页面时间戳constnow=newDate()this.srcDate=`${now.getMonth()+1}/${now.getDate().toString().padStart(2,'0')}`this.currentTime=`${now.getHours().toString().padStart(2,'0')}:${now.getMinutes().toString().padStart(2,'0')}`// ② 加载报告数据(异步)this.loadReport()}[HomePage.ets](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/pages/HomePage.ets) 在 aboutToAppear 中做更复杂的初始化:
aboutToAppear():void{// ① 读取 AppStorage 全局数据constname:string|undefined=AppStorage.get<string>('user_name')if(name)this.userName=name// ② 异步加载数据this.loadRecent()this.checkCoupleStatus()// ③ 初始化问候语constnow:Date=newDate()consth:number=now.getHours()this.greetingPeriod=h<12?'早安':h<18?'下午好':'晚上好'this.greetingDate=`${now.getMonth()+1}月${now.getDate()}日`this.currentTime=`${h.toString().padStart(2,'0')}:${now.getMinutes().toString().padStart(2,'0')}`// ④ 触发入场动画(setTimeout 确保 build() 已完成)setTimeout(()=>this.startEntranceAnimation(),50)}2.2 动画触发
Canvas 动画组件在aboutToAppear中触发animateTo:
// HandwritingRadar.etsaboutToAppear():void{animateTo({duration:AppAnimations.RADAR_DRAW_DURATION,curve:AppAnimations.CURVE_SMOOTH},()=>{this.drawProgress=1// 触发 @Watch → draw() 重绘})}2.3 定时器启动
[EmptyState.ets](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/common/components/EmptyState.ets) 在aboutToAppear中启动呼吸脉冲定时器:
aboutToAppear():void{// 入场动画animateTo({duration:600,curve:Curve.FastOutSlowIn},()=>{this.illustrationOpacity=1this.illustrationOffset=0})// 启动呼吸脉冲this.pulseTimer=setInterval(()=>{animateTo({duration:800,curve:Curve.EaseOut},()=>{this.pulseScale=this.pulseScale>1.05?1:1.06})},1600)}2.4 aboutToAppear 中的常见操作
| 操作 | 代码模式 | 适用场景 | 示例页面 |
|---|---|---|---|
| 读取 AppStorage | AppStorage.get<T>('key') | 全局数据读取 | HomePage |
| 异步数据加载 | this.loadData()(async) | DB/网络请求 | ReportDetailPage |
| 动画触发 | animateTo({...}, () => { state = value }) | 入场动画 | HandwritingRadar |
| 定时器启动 | this.timer = setInterval(...) | 循环动画 | EmptyState |
| 状态初始化 | this.state = defaultValue | 数据准备 | CapturePage |
提示:
aboutToAppear中无法获取组件在 UI 中的尺寸(此时build()尚未执行)。需要尺寸信息应在build()的onReady或onAreaChange中获取。
三、aboutToDisappear:资源清理
3.1 定时器清理
使用setInterval/setTimeout的组件必须在aboutToDisappear中清理:
// EmptyState.etsprivatepulseTimer:number=-1aboutToDisappear():void{if(this.pulseTimer!==-1){clearInterval(this.pulseTimer)this.pulseTimer=-1}}3.2 相机资源释放
[CapturePage.ets](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/pages/CapturePage.ets) 在页面销毁时释放相机资源:
// CapturePage 中的资源释放方法privatereleaseCamera(){try{if(this.captureSession){this.captureSession.release()// 释放拍照会话}if(this.cameraInput){this.cameraInput.close()// 关闭相机输入}}catch(error){hilog.error(0x0000,TAG,'释放相机资源失败: %{public}s',JSON.stringify(error))}}3.3 应该清理的资源类型
| 资源类型 | 清理方式 | 未清理的后果 |
|---|---|---|
setInterval | clearInterval() | 内存泄漏、页面销毁后仍在执行 |
setTimeout | clearTimeout() | 页面销毁后回调仍然触发 |
| 相机资源 | .release()/.close() | 相机被占用,其他应用无法使用 |
| 文件句柄 | .close() | 文件锁、文件描述符泄漏 |
| 事件监听 | .off()/.unsubscribe() | 内存泄漏、重复回调 |
关键原则:任何在
aboutToAppear中"打开"的资源,都必须在aboutToDisappear中"关闭"。
四、onPageShow:页面刷新
4.1 每次显示时刷新数据
onPageShow在页面每次显示给用户时都会调用——包括首次加载、从其他页面返回、从后台切换回前台:
// HomePage.etsonPageShow():void{// 每次回到首页都刷新数据this.loadRecent()this.checkCoupleStatus()}4.2 刷新场景对比
| 场景 | 触发方法 | 是否需要刷新 |
|---|---|---|
| 首次进入页面 | aboutToAppear→onPageShow | ✅ |
| 从子页面返回 | onPageShow(aboutToAppear 不触发) | ✅ |
| 切换 Tab 回到页面 | onPageShow | ✅ |
| 从后台回到前台 | onPageShow | 视情况 |
| 屏幕旋转 | 重建组件 → 完整生命周期 | ✅ |
4.3 onPageShow vs aboutToAppear
@Entry@Componentstruct HomePage{recentItems:RecentItem[]=[]// ① 组件首次创建时触发aboutToAppear():void{// 只做一次的事情:初始化问候语、启动动画this.greetingPeriod='下午好'this.loadRecent()// 首次加载数据setTimeout(()=>this.startEntranceAnimation(),50)}// ② 每次页面可见时触发(返回时也会)onPageShow():void{// 每次刷新数据(因为可能在子页面创建了新报告)this.loadRecent()this.checkCoupleStatus()}}区别总结:
| 对比 | aboutToAppear | onPageShow |
|---|---|---|
| 触发次数 | 组件整个生命周期只触发 1 次 | 页面每次显示都触发 |
| 适用操作 | 初始化、入场动画 | 数据刷新、状态同步 |
| 适用组件 | @Entry 和 @Component | 仅 @Entry 页面组件 |
| 从后台返回 | ❌ 不触发 | ✅ 触发 |
五、生命周期在 Canvas 组件中的应用
5.1 Canvas 组件的 onReady
Canvas 组件有一个不同于一般组件的特殊回调——onReady,在 Canvas 上下文就绪后调用:
@Componentexportstruct HandwritingRadar{privatecontext:CanvasRenderingContext2D=newCanvasRenderingContext2D(newRenderingContextSettings(true))aboutToAppear():void{// ① 触发动画(此时 Canvas 尚未就绪,不能绘图)animateTo({duration:800},()=>{this.drawProgress=1})}onDrawProgressChange():void{// ② @Watch 回调 → 绘图(此时 Canvas 已经就绪)this.draw()}build(){Canvas(this.context).width(this.radarSize).height(this.radarSize).onReady(()=>{// ③ Canvas 上下文就绪 → 首次绘图this.draw()})}}完整的 Canvas 生命周期时序:
aboutToAppear→ 触发animateTo(仅改变 @State,不绘图)build()→ Canvas 组件创建Canvas.onReady→ Canvas 绘图上下文就绪 → 首次draw()animateTo持续更新drawProgress→@Watch持续调用draw()aboutToDisappear→ 组件销毁
5.2 页面转场动画
页面级的转场动画通过pageTransition方法配置:
// CapturePage.etspageTransition(){PageTransitionEnter({duration:350,curve:Curve.FastOutSlowIn}).slide(SlideEffect.Right)// 从右侧滑入PageTransitionExit({duration:300,curve:Curve.FastOutSlowIn}).slide(SlideEffect.Right)// 向右侧滑出}六、生命周期的调试技巧
6.1 使用 hilog 追踪生命周期
constTAG='HomePage'aboutToAppear():void{hilog.info(0x0000,TAG,'aboutToAppear')// ...}onPageShow():void{hilog.info(0x0000,TAG,'onPageShow')// ...}aboutToDisappear():void{hilog.info(0x0000,TAG,'aboutToDisappear')// ...}6.2 生命周期调用堆栈示例
// 打开 App → 首页 [HomePage] aboutToAppear [HomePage] build [HomePage] onPageShow // 跳转到拍照页 [HomePage] onPageHide [CapturePage] aboutToAppear [CapturePage] build [CapturePage] onPageShow // 拍照后返回首页 [CapturePage] aboutToDisappear [HomePage] onPageShow ← 注意:没有 aboutToAppear // 关闭 App [HomePage] aboutToDisappear七、生命周期最佳实践清单
7.1 检查清单
在编写每个组件时,检查以下问题:
| 问题 | 应该在哪个生命周期处理 |
|---|---|
| 需要加载数据吗? | aboutToAppear(首次) /onPageShow(每次) |
| 需要入场动画吗? | aboutToAppear |
| 使用了 setInterval 吗? | 启动:aboutToAppear/ 清理:aboutToDisappear |
| 使用了相机/传感器吗? | 初始化:aboutToAppear/ 释放:aboutToDisappear |
| 返回时需要刷新数据吗? | onPageShow |
| 需要在 Canvas 上绘图吗? | onReady中首次绘制,@Watch中更新 |
7.2 常见错误
// ❌ 错误:在 aboutToAppear 中执行异步操作但没处理竞态aboutToAppear():void{this.loadData()// 异步,可能组件已销毁才返回}// ✅ 正确:使用标志位防止销毁后更新状态privateisActive:boolean=falseaboutToAppear():void{this.isActive=truethis.loadData()}aboutToDisappear():void{this.isActive=false}privateasyncloadData():Promise<void>{constdata=awaitfetchData()if(this.isActive){this.stateData=data// 组件还在才更新}}八、EntryAbility 生命周期
8.1 UIAbility 生命周期
项目入口 [EntryAbility.ets](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/EntryAbility.ets) 使用应用级生命周期方法:
exportdefaultclassEntryAbilityextendsUIAbility{// 应用创建onCreate(want:Want,launchParam:AbilityConstant.LaunchParam):void{hilog.info(0x0000,'HandwritingAnalysis','Ability onCreate')}// 应用销毁onDestroy():void{hilog.info(0x0000,'HandwritingAnalysis','Ability onDestroy')}// 窗口创建(加载页面、初始化DB)onWindowStageCreate(windowStage:window.WindowStage):void{// 初始化本地数据库DatabaseService.getInstance().init(this.context).then(async()=>{constuser=awaitUserDao.ensureLocalUser('鹿鹿用户')AppStorage.setOrCreate<number>('user_id',user.id??1)AppStorage.setOrCreate<string>('user_name',user.name)})// 加载首屏页面windowStage.loadContent('pages/LoginPage',...)}// 窗口销毁onWindowStageDestroy():void{...}// 切到前台onForeground():void{...}// 切到后台onBackground():void{...}}8.2 两级生命周期对比
| 层级 | 入口 | 用途 | 示例 |
|---|---|---|---|
| 应用级 | UIAbility方法 | 全局初始化、数据库 | onCreate/onWindowStageCreate |
| 页面级 | @Entry组件方法 | 页面加载、刷新 | aboutToAppear/onPageShow |
| 组件级 | @Component方法 | 组件初始化、清理 | aboutToAppear/aboutToDisappear |
总结
ArkUI 组件生命周期是管理数据、动画和资源的核心工具。本文通过项目中的实际代码案例,总结了各生命周期方法的调用时机和使用场景:
- aboutToAppear:组件初始化——加载数据、触发动画、启动定时器
- aboutToDisappear:资源清理——清除定时器、释放相机、关闭文件句柄
- onPageShow:页面刷新——每次显示时重新加载数据
- onPageHide:页面离开——暂停动画或资源消耗操作
- onReady:Canvas 就绪——首次绘制
核心原则:成对出现——aboutToAppear 中打开的资源,在 aboutToDisappear 中关闭;aboutToAppear 中加载的数据,在 onPageShow 中刷新。
下一篇文章将进入第五模块:数据持久化——RDB 关系型数据库,从鸿蒙原生数据库的使用开始讲解。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
参考资源:
- ArkUI 组件生命周期
- aboutToAppear 参考
- onPageShow 页面显示
- UIAbility 生命周期
- [HomePage 项目源码](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/pages/HomePage.ets)
- [CapturePage 项目源码](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/pages/CapturePage.ets)
- [EmptyState 组件源码](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/common/components/EmptyState.ets)
- [EntryAbility 项目源码](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/EntryAbility.ets)
- HarmonyOS 官方示例
