【学习目标】

  1. 理解生命周期的核心概念,区分自定义组件生命周期和页面生命周期的本质差异;
  2. 掌握核心生命周期方法,明确各方法的触发时机及使用规范;
  3. 掌握自定义组件/页面的完整生命周期流程,理解嵌套组件的生命周期调用时序;
  4. 能在正确的生命周期阶段执行对应操作(初始化、资源清理、数据请求等),规避内存泄漏等问题;
  5. 结合实战示例,理解生命周期在实际开发中的应用场景。

一、生命周期核心概念

1.1 什么是生命周期?

页面/组件的生命周期,是指用@Component/@ComponentV2装饰的自定义组件从创建、渲染、更新到销毁的整个阶段,每个阶段都有明确的触发时机和核心职责。

简单来说:

  • 生命周期规定了「组件/页面在什么时间该做什么事」;
  • 遵循生命周期规范,能避免无效操作、内存泄漏、数据不一致等问题。

1.2 生命周期分类

  • 自定义组件通用生命周期:包含aboutToAppearonDidBuildaboutToDisappear,仅需@Component/@ComponentV2装饰器即可生效,所有自定义组件通用;其中onDidBuild仅在首次渲染时触发,后续组件重新渲染不再执行。
  • 页面专属生命周期:在自定义组件生命周期基础上,新增onPageShowonPageHide,需@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 自定义组件生命周期流程

框架创建组件实例

初始化成员变量(定义顺序)

执行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 内存资源占用演示(真机设备)

操作步骤:
  1. 注释NextPageaboutToDisappearonPageHide中定时器清理代码:
// if (this.pageTimerId) {
//   clearInterval(this.pageTimerId);
//   this.pageTimerId = null;
// }
  1. 重复操作:首页→NextPage之间多次切换,观察内存:通过Profiler工具查看,内存持续上涨无释放;
  2. 恢复清理代码后重复操作:内存占用稳定,定时器被正常释放。
未清理定时器

生命周期中内存资源占用

清理定时器

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

生命周期中内存资源及时释放

七、常见错误与解决方案

错误1:子组件实现页面专属生命周期方法

  • 问题表现:子组件中定义onPageShow/onPageHide,运行后无日志输出,方法完全不生效;
  • 解决方案:仅在@Entry + @Component装饰的根页面实现onPageShow/onPageHide,子组件仅保留通用生命周期(aboutToAppear/onDidBuild/aboutToDisappear)。

错误2:aboutToAppear 执行同步耗时操作

  • 问题表现:同步阻塞操作(如大数据解析、复杂计算)导致首屏渲染卡顿、页面启动超时;
  • 解决方案:
    1. 将耗时操作拆分为异步任务(setTimeout/Promise),避免阻塞渲染;
    2. 非首屏关键操作延后至onPageShow或用户交互事件中执行;

错误3:aboutToDisappear中执行异步操作或修改状态

  • 问题表现:组件销毁流程阻塞、垃圾回收失败(内存泄漏),或触发二次渲染导致页面闪屏;
  • 解决方案:
    1. aboutToDisappear仅保留同步清理逻辑(如clearInterval/clearTimeout、取消网络请求);
    2. 需异步清理的操作提前在onPageHide中执行;
    3. 禁止修改任何响应式状态变量如@Link会导致程序不稳定。

错误4:资源未清理

  • 问题表现:页面销毁后定时器仍运行、网络请求仍发送,日志持续输出,内存占用持续上涨;
  • 解决方案:
    1. 将定时器ID、请求控制器等存入组件私有变量;
    2. aboutToDisappear中强制清理资源(如clearInterval/取消请求),确保100%释放。

八、仓库代码

  • 工程名称:LifeCycleDemo
  • 仓库地址:https://gitee.com/HarmonyOS-UI-Basics/harmony-os-ui-basics.git

九、下节预告

下一节我们将学习 ArkTS 全局状态存储(LocalStorage/AppStorage/PersistentStorage)

  1. 掌握 LocalStorage(页面级)和 AppStorage(应用级)的核心用法;
  2. 结合生命周期实现全局状态的初始化、更新与清理;
  3. 掌握 PersistentStorage 基础持久化,解决应用重启数据丢失问题;
  4. 实战:实现跨页面数据共享与持久化,规避数据不一致问题。
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐