【学习目标】
一、生命周期核心概念
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 自定义组件生命周期流程
#mermaid-svg-isaYMrbcmguI5yWA{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-isaYMrbcmguI5yWA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-isaYMrbcmguI5yWA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-isaYMrbcmguI5yWA .error-icon{fill:#552222;}#mermaid-svg-isaYMrbcmguI5yWA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-isaYMrbcmguI5yWA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-isaYMrbcmguI5yWA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-isaYMrbcmguI5yWA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-isaYMrbcmguI5yWA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-isaYMrbcmguI5yWA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-isaYMrbcmguI5yWA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-isaYMrbcmguI5yWA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-isaYMrbcmguI5yWA .marker.cross{stroke:#333333;}#mermaid-svg-isaYMrbcmguI5yWA svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-isaYMrbcmguI5yWA p{margin:0;}#mermaid-svg-isaYMrbcmguI5yWA .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-isaYMrbcmguI5yWA .cluster-label text{fill:#333;}#mermaid-svg-isaYMrbcmguI5yWA .cluster-label span{color:#333;}#mermaid-svg-isaYMrbcmguI5yWA .cluster-label span p{background-color:transparent;}#mermaid-svg-isaYMrbcmguI5yWA .label text,#mermaid-svg-isaYMrbcmguI5yWA span{fill:#333;color:#333;}#mermaid-svg-isaYMrbcmguI5yWA .node rect,#mermaid-svg-isaYMrbcmguI5yWA .node circle,#mermaid-svg-isaYMrbcmguI5yWA .node ellipse,#mermaid-svg-isaYMrbcmguI5yWA .node polygon,#mermaid-svg-isaYMrbcmguI5yWA .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-isaYMrbcmguI5yWA .rough-node .label text,#mermaid-svg-isaYMrbcmguI5yWA .node .label text,#mermaid-svg-isaYMrbcmguI5yWA .image-shape .label,#mermaid-svg-isaYMrbcmguI5yWA .icon-shape .label{text-anchor:middle;}#mermaid-svg-isaYMrbcmguI5yWA .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-isaYMrbcmguI5yWA .rough-node .label,#mermaid-svg-isaYMrbcmguI5yWA .node .label,#mermaid-svg-isaYMrbcmguI5yWA .image-shape .label,#mermaid-svg-isaYMrbcmguI5yWA .icon-shape .label{text-align:center;}#mermaid-svg-isaYMrbcmguI5yWA .node.clickable{cursor:pointer;}#mermaid-svg-isaYMrbcmguI5yWA .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-isaYMrbcmguI5yWA .arrowheadPath{fill:#333333;}#mermaid-svg-isaYMrbcmguI5yWA .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-isaYMrbcmguI5yWA .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-isaYMrbcmguI5yWA .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-isaYMrbcmguI5yWA .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-isaYMrbcmguI5yWA .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-isaYMrbcmguI5yWA .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-isaYMrbcmguI5yWA .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-isaYMrbcmguI5yWA .cluster text{fill:#333;}#mermaid-svg-isaYMrbcmguI5yWA .cluster span{color:#333;}#mermaid-svg-isaYMrbcmguI5yWA div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-isaYMrbcmguI5yWA .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-isaYMrbcmguI5yWA rect.text{fill:none;stroke-width:0;}#mermaid-svg-isaYMrbcmguI5yWA .icon-shape,#mermaid-svg-isaYMrbcmguI5yWA .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-isaYMrbcmguI5yWA .icon-shape p,#mermaid-svg-isaYMrbcmguI5yWA .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-isaYMrbcmguI5yWA .icon-shape rect,#mermaid-svg-isaYMrbcmguI5yWA .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-isaYMrbcmguI5yWA .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-isaYMrbcmguI5yWA .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-isaYMrbcmguI5yWA :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
框架创建组件实例
初始化成员变量(定义顺序)
执行aboutToAppear
执行build(仅解析结构,不渲染子组件)
执行onDidBuild(仅首次渲染)
组件销毁前执行aboutToDisappear
四、自定义组件生命周期示例
// 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 内存资源占用演示(真机设备)
操作步骤:
// if (this.pageTimerId) {
// clearInterval(this.pageTimerId);
// this.pageTimerId = null;
// }
未清理定时器

清理定时器


七、常见错误与解决方案
错误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)





