鸿蒙应用开发UI基础第二十一节:自定义组件与页面的生命周期
本文详细介绍了鸿蒙应用开发中的生命周期机制,主要包含以下内容: 生命周期核心概念:区分自定义组件生命周期(aboutToAppear、onDidBuild、aboutToDisappear)和页面专属生命周期(onPageShow、onPageHide)的差异。 生命周期触发时机与规范: aboutToAppear:组件实例创建后,用于初始化数据 onDidBuild:首次渲染完成后,适合轻量操作
·
【学习目标】
- 理解生命周期的核心概念,区分自定义组件生命周期和页面生命周期的本质差异;
- 掌握核心生命周期方法,明确各方法的触发时机及使用规范;
- 掌握自定义组件/页面的完整生命周期流程,理解嵌套组件的生命周期调用时序;
- 能在正确的生命周期阶段执行对应操作(初始化、资源清理、数据请求等),规避内存泄漏等问题;
- 结合实战示例,理解生命周期在实际开发中的应用场景。
一、生命周期核心概念
1.1 什么是生命周期?
页面/组件的生命周期,是指用@Component/@ComponentV2装饰的自定义组件从创建、渲染、更新到销毁的整个阶段,每个阶段都有明确的触发时机和核心职责。
简单来说:
- 生命周期规定了「组件/页面在什么时间该做什么事」;
- 遵循生命周期规范,能避免无效操作、内存泄漏、数据不一致等问题。
1.2 生命周期分类
- 自定义组件通用生命周期:包含
aboutToAppear、onDidBuild、aboutToDisappear,仅需@Component/@ComponentV2装饰器即可生效,所有自定义组件通用;其中onDidBuild仅在首次渲染时触发,后续组件重新渲染不再执行。 - 页面专属生命周期:在自定义组件生命周期基础上,新增
onPageShow、onPageHide,需@Entry + @Component装饰器配合使用,仅在根页面生效,赋予页面前后台切换能力。
二、工程目录结构
本节示例基于鸿蒙 API12+ Stage 模型工程,核心目录结构如下(仅展示关键文件):
LifeCycleDemo/
├── AppScope/
│ └── app.json5 // 应用全局配置
├── entry/
│ ├── src/
│ │ ├── main/
│ │ │ ├── ets/
│ │ │ │ ├── entryability/
│ │ │ │ │ └── EntryAbility.ets // 应用入口能力
│ │ │ │ ├── components/ // 自定义组件目录
│ │ │ │ │ └── LifeCycleDemoComponent.ets // 自定义组件示例
│ │ │ │ ├── pages/ // 页面目录
│ │ │ │ │ ├── Index.ets // 首页(跳转NextPage)
│ │ │ │ │ ├── NextPage.ets // 第二层页面
│ │ │ │ │ └── OtherPage.ets // 第三层页面
│ │ │ ├── resources/ // 资源目录(图片、字符串等)
│ │ │ └── module.json5 // 模块配置
│ │ └── oh-package.json5 // 依赖配置
│ └── build-profile.json5 // 构建配置
└── hvigorfile.ts // 构建脚本
三、核心生命周期方法详解
3.1 自定义组件核心生命周期
| 方法名 | 触发时机 | 核心作用 | 禁止操作 |
|---|---|---|---|
aboutToAppear |
组件实例创建完成后、build()执行前 |
初始化组件数据(修改@State、绑定变量)、发起首屏页面数据异步请求 |
执行同步耗时操作(阻塞渲染) |
onDidBuild |
组件首次渲染build()完成后 |
埋点上报、获取组件尺寸等轻量无UI影响操作 | 修改响应式状态变量(触发二次渲染)、耗时操作 |
aboutToDisappear |
组件销毁前 | 清理资源(清除定时器、取消网络请求、解绑监听) | 修改状态变量、使用async/await(阻碍垃圾回收) |
3.2 页面专属生命周期(仅 @Entry + @Component 生效)
| 方法名 | 触发时机 | 核心作用 | 注意事项 |
|---|---|---|---|
onPageShow |
页面显示时(首次进入/切回前台/路由返回) | 恢复数据、刷新非关键请求、启动动画 | 需做防抖处理,避免频繁触发 |
onPageHide |
页面隐藏时(跳转页面/切后台) | 暂停操作(定时器/媒体)、保存临时状态 | 避免不必要耗时操作,优先轻量处理 |
3.3 自定义组件生命周期流程
四、自定义组件生命周期示例
// entry/src/main/ets/components/LifeCycleDemoComponent.ets
import { hilog } from '@kit.PerformanceAnalysisKit';
@Component
export struct LifeCycleDemoComponent {
// 响应式状态变量
@State count: number = 0;
private timerId: number | null = null;
// 1. 组件即将渲染
aboutToAppear() {
hilog.info(0x0000, 'testTag', '[子组件] LifeCycleDemoComponent: aboutToAppear');
// 允许修改状态变量
this.count = 10;
}
// 2. 组件首次渲染build完成后触发(仅执行一次)
onDidBuild() {
hilog.info(0x0000, 'testTag', '[子组件] LifeCycleDemoComponent: onDidBuild');
// 轻量 UI 操作(如获取组件尺寸,禁止修改状态,很少用到)
}
// 3. 组件即将销毁
aboutToDisappear() {
hilog.info(0x0000, 'testTag', '[子组件] LifeCycleDemoComponent: aboutToDisappear');
// 彻底清理资源(防止内存泄漏)
if (this.timerId) {
clearInterval(this.timerId);
this.timerId = null;
}
hilog.info(0x0000, 'testTag', '[子组件] 销毁前count值:%{public}d', this.count);
// 禁止:修改状态变量、使用async/await、新增异步操作
}
// 页面特有生命周期在子组件中实现不会执行
onPageShow(): void {
hilog.info(0x0000, 'testTag', '[子组件] 组件已经显示:不会执行');
}
onPageHide(): void {
hilog.info(0x0000, 'testTag', '[子组件] 组件已经隐藏:不会执行');
}
build() {
Column({ space: 10 }) {
Text(`自定义组件计数:${this.count}`)
.fontSize(20)
.fontColor('#333');
Button("启动定时器(子组件)")
.onClick(() => {
// 事件回调中允许启动异步资源
this.timerId = setInterval(() => {
this.count++;
hilog.info(0x0000, 'testTag', '[子组件] 定时器更新count:%{public}d', this.count);
}, 1000);
})
.margin({ top: 10 });
Button("重置计数")
.onClick(() => {
this.count = 0;
})
.margin({ top: 10 });
}
.padding(20)
.backgroundColor('#f5f5f5')
.borderRadius(12);
}
}
五、页面生命周期示例
5.1 首页(Index.ets,跳转NextPage)
// entry/src/main/ets/pages/Index.ets
import { router } from '@kit.ArkUI';
import { hilog } from '@kit.PerformanceAnalysisKit';
@Entry
@Component
struct Index {
build() {
Column({ space: 20 }) {
Text("生命周期演示首页")
.fontSize(24)
.fontWeight(FontWeight.Bold);
Text("点击按钮跳转到 NextPage,查看控制台日志")
.fontSize(16)
.fontColor('#666');
Button("跳转到 NextPage")
.onClick(() => {
router.pushUrl({
url: "pages/NextPage"
}).catch((err: BusinessError) => {
hilog.error(0x0000, 'testTag', '[首页] 跳转失败:%{public}s', err.message);
});
})
.backgroundColor('#007dff')
.fontColor(Color.White)
.margin({ top: 10 });
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center);
}
}
5.2 第二层页面(NextPage.ets)
// entry/src/main/ets/pages/NextPage.ets
import { router } from '@kit.ArkUI';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { LifeCycleDemoComponent } from '../components/LifeCycleDemoComponent';
@Entry
@Component
struct NextPage {
@State showChild: boolean = true; // 控制子组件显示/隐藏
private pageTimerId: number | null = null;
// ========== 组件通用生命周期 ==========
aboutToAppear() {
hilog.info(0x0000, 'testTag', '[页面] NextPage: aboutToAppear');
}
onDidBuild() {
hilog.info(0x0000, 'testTag', '[页面] NextPage: onDidBuild');
// 页面首次渲染build完成后执行(仅一次)
//【关键】父组件onDidBuild执行完成后,子组件才会触发aboutToAppear(懒展开)
}
aboutToDisappear() {
hilog.info(0x0000, 'testTag', '[页面] NextPage: aboutToDisappear');
// 最终清理页面级资源
if (this.pageTimerId) {
clearInterval(this.pageTimerId);
this.pageTimerId = null;
}
}
// ========== 页面特有生命周期(核心演示onPageHide/onPageShow) ==========
onPageShow() {
hilog.info(0x0000, 'testTag', '[页面] NextPage: onPageShow');
// 启动页面级定时器(演示用)
this.pageTimerId = setInterval(() => {
hilog.info(0x0000, 'testTag', '[页面] NextPage 定时器运行中...');
}, 2000);
}
onPageHide() {
hilog.info(0x0000, 'testTag', '[页面] NextPage: onPageHide');
// 暂停页面级定时器(核心:跳转OtherPage时触发)
if (this.pageTimerId) {
clearInterval(this.pageTimerId);
this.pageTimerId = null;
}
}
build() {
Column({ space: 20 }) {
Text("NextPage(第二层页面)")
.fontSize(24)
.fontWeight(FontWeight.Bold);
Text("跳转OtherPage会触发onPageHide,返回会触发onPageShow")
.fontSize(16)
.fontColor('#666');
// 控制子组件显示/隐藏
Button(this.showChild ? '隐藏子组件' : '显示子组件')
.margin(10)
.backgroundColor('#FF007DFF')
.onClick(() => {
this.showChild = !this.showChild;
});
if (this.showChild) {
LifeCycleDemoComponent();
}
// 跳转第三层页面OtherPage
Button("跳转到 OtherPage(第三层)")
.onClick(() => {
router.pushUrl({
url: "pages/OtherPage"
}).catch((err: BusinessError) => {
hilog.error(0x0000, 'testTag', '[NextPage] 跳转失败:%{public}s', err.message);
});
})
.backgroundColor('#FF9900')
.fontColor(Color.White)
.margin({ top: 10 });
// 返回首页
Button("返回首页")
.onClick(() => {
router.back()
})
.backgroundColor('#666')
.fontColor(Color.White)
.margin({ top: 10 });
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.padding(20);
}
}
5.3 第三层页面(OtherPage.ets)
// entry/src/main/ets/pages/OtherPage.ets
import { router } from '@kit.ArkUI';
import { hilog } from '@kit.PerformanceAnalysisKit';
@Entry
@Component
struct OtherPage {
// ========== 组件通用生命周期 ==========
aboutToAppear() {
hilog.info(0x0000, 'testTag', '[页面] OtherPage: aboutToAppear');
}
onDidBuild() {
hilog.info(0x0000, 'testTag', '[页面] OtherPage: onDidBuild');
}
aboutToDisappear() {
hilog.info(0x0000, 'testTag', '[页面] OtherPage: aboutToDisappear');
}
// ========== 页面特有生命周期 ==========
onPageShow() {
hilog.info(0x0000, 'testTag', '[页面] OtherPage: onPageShow');
}
onPageHide() {
hilog.info(0x0000, 'testTag', '[页面] OtherPage: onPageHide');
}
build() {
Column({ space: 20 }) {
Text("OtherPage(第三层页面)")
.fontSize(24)
.fontWeight(FontWeight.Bold);
Button("返回 NextPage(第二层)")
.onClick(() => {
router.back()
})
.backgroundColor('#007dff')
.fontColor(Color.White)
.margin({ top: 10 });
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.padding(20);
}
}
六、生命周期日志与核心时序验证
Index页面未添加生命周期回调,仅打印其他页面的生命周期日志(过滤无关内容)。
6.1 首页点击「跳转到 NextPage」
[页面] NextPage: aboutToAppear // 父组件aboutToAppear执行
[页面] NextPage: onDidBuild // 父组件onDidBuild执行(懒展开:执行完后子组件才启动)
[子组件] LifeCycleDemoComponent: aboutToAppear // 子组件aboutToAppear执行
[子组件] LifeCycleDemoComponent: onDidBuild // 子组件onDidBuild执行
[页面] NextPage: onPageShow // 父页面onPageShow(所有子组件渲染完成后触发)
核心结论(懒展开特性):
应用冷启动下嵌套组件初始化流程:父组件 aboutToAppear → 父组件 build → 父组件 onDidBuild → 子组件 aboutToAppear → 子组件 build → 子组件 onDidBuild → 父组件 onPageShow
- 父组件执行
onDidBuild后,子组件才触发aboutToAppear,是鸿蒙自定义组件的懒展开/懒渲染特性; - 懒展开核心价值:提升首屏性能,父组件优先完成基础渲染,子组件延迟初始化,避免首屏阻塞。
6.2 NextPage点击「隐藏子组件」
[子组件] LifeCycleDemoComponent: aboutToDisappear
6.3 NextPage点击「显示子组件」
[子组件] LifeCycleDemoComponent: aboutToAppear
[子组件] LifeCycleDemoComponent: onDidBuild
说明:
if/else控制组件显示/隐藏并非“视觉隐藏”,而是从组件树中新增/删除组件,因此会触发完整的生命周期。
6.4 NextPage点击「跳转到 OtherPage(第三层)」
[页面] OtherPage: aboutToAppear
[页面] OtherPage: onDidBuild
[页面] NextPage: onPageHide
[页面] OtherPage: onPageShow
6.5 点击「返回 NextPage(第二层)」
[页面] OtherPage: onPageHide
[页面] NextPage: onPageShow
[页面] OtherPage: aboutToDisappear
6.6 NextPage点击「返回首页」
[页面] NextPage: onPageHide
[页面] NextPage: aboutToDisappear
[子组件] LifeCycleDemoComponent: aboutToDisappear
6.7 内存资源占用演示(真机设备)
操作步骤:
- 注释
NextPage的aboutToDisappear和onPageHide中定时器清理代码:
// if (this.pageTimerId) {
// clearInterval(this.pageTimerId);
// this.pageTimerId = null;
// }
- 重复操作:首页→NextPage之间多次切换,观察内存:通过Profiler工具查看,内存持续上涨无释放;
- 恢复清理代码后重复操作:内存占用稳定,定时器被正常释放。
未清理定时器

清理定时器


七、常见错误与解决方案
错误1:子组件实现页面专属生命周期方法
- 问题表现:子组件中定义
onPageShow/onPageHide,运行后无日志输出,方法完全不生效; - 解决方案:仅在
@Entry + @Component装饰的根页面实现onPageShow/onPageHide,子组件仅保留通用生命周期(aboutToAppear/onDidBuild/aboutToDisappear)。
错误2:aboutToAppear 执行同步耗时操作
- 问题表现:同步阻塞操作(如大数据解析、复杂计算)导致首屏渲染卡顿、页面启动超时;
- 解决方案:
- 将耗时操作拆分为异步任务(
setTimeout/Promise),避免阻塞渲染; - 非首屏关键操作延后至
onPageShow或用户交互事件中执行;
- 将耗时操作拆分为异步任务(
错误3:aboutToDisappear中执行异步操作或修改状态
- 问题表现:组件销毁流程阻塞、垃圾回收失败(内存泄漏),或触发二次渲染导致页面闪屏;
- 解决方案:
aboutToDisappear仅保留同步清理逻辑(如clearInterval/clearTimeout、取消网络请求);- 需异步清理的操作提前在
onPageHide中执行; - 禁止修改任何响应式状态变量如
@Link会导致程序不稳定。
错误4:资源未清理
- 问题表现:页面销毁后定时器仍运行、网络请求仍发送,日志持续输出,内存占用持续上涨;
- 解决方案:
- 将定时器ID、请求控制器等存入组件私有变量;
- 在
aboutToDisappear中强制清理资源(如clearInterval/取消请求),确保100%释放。
八、仓库代码
- 工程名称:LifeCycleDemo
- 仓库地址:https://gitee.com/HarmonyOS-UI-Basics/harmony-os-ui-basics.git
九、下节预告
下一节我们将学习 ArkTS 全局状态存储(LocalStorage/AppStorage/PersistentStorage)
- 掌握 LocalStorage(页面级)和 AppStorage(应用级)的核心用法;
- 结合生命周期实现全局状态的初始化、更新与清理;
- 掌握 PersistentStorage 基础持久化,解决应用重启数据丢失问题;
- 实战:实现跨页面数据共享与持久化,规避数据不一致问题。
更多推荐




所有评论(0)