ArkTS 中页面和组件的生命周期
📌 快速概览
在 HarmonyOS ArkTS 中,生命周期是指页面、组件从创建到销毁的整个过程中,系统自动调用的一系列钩子函数。理解生命周期对于正确管理资源、优化性能至关重要。
🎯 一、生命周期分类
1.1 三个层级的生命周期
| 层级 |
说明 |
典型场景 |
| UIAbility 生命周期 |
应用的 Ability 级别生命周期 |
应用启动、后台、销毁 |
| 页面生命周期 |
页面级别的生命周期 |
页面显示、隐藏、返回 |
| 组件生命周期 |
自定义组件级别的生命周期 |
组件创建、更新、销毁 |
1.2 生命周期概览图
UIAbility 生命周期
├── onCreate() // Ability 创建
├── onWindowStageCreate() // 窗口舞台创建
├── onForeground() // 进入前台
├── onBackground() // 进入后台
└── onDestroy() // Ability 销毁
页面生命周期
├── onPageShow() // 页面显示
├── onPageHide() // 页面隐藏
└── onBackPress() // 返回按钮
组件生命周期
├── aboutToAppear() // 组件即将出现
├── aboutToDisappear() // 组件即将消失
└── aboutToReuse() // 组件复用(@Reusable)
📖 二、UIAbility 生命周期
2.1 生命周期钩子
| 钩子函数 |
调用时机 |
用途 |
onCreate() |
Ability 首次创建 |
初始化全局数据 |
onWindowStageCreate() |
窗口舞台创建 |
加载首页 |
onForeground() |
进入前台 |
恢复暂停的任务 |
onBackground() |
进入后台 |
暂停任务、保存数据 |
onWindowStageDestroy() |
窗口舞台销毁 |
释放窗口资源 |
onDestroy() |
Ability 销毁 |
释放资源 |
2.2 生命周期流程
正常启动流程
应用启动
↓
onCreate() // 创建 Ability
↓
onWindowStageCreate() // 创建窗口舞台,加载首页
↓
onForeground() // 进入前台
↓
应用运行中
↓
(用户按 Home 键)
↓
onBackground() // 进入后台
↓
(用户重新打开应用)
↓
onForeground() // 重新进入前台
↓
(用户关闭应用)
↓
onWindowStageDestroy() // 销毁窗口舞台
↓
onDestroy() // 销毁 Ability
2.3 代码示例
import UIAbility from "@ohos.app.ability.UIAbility";
import window from "@ohos.window";
import Want from "@ohos.app.ability.Want";
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
console.info("[EntryAbility] onCreate");
AppStorage.SetOrCreate("userId", "");
AppStorage.SetOrCreate("token", "");
this.checkLoginStatus();
}
onWindowStageCreate(windowStage: window.WindowStage) {
console.info("[EntryAbility] onWindowStageCreate");
windowStage.loadContent("pages/Index", (err, data) => {
if (err.code) {
console.error("加载页面失败:", err.message);
return;
}
console.info("加载页面成功");
});
}
onForeground() {
console.info("[EntryAbility] onForeground");
this.startTimer();
this.refreshData();
}
onBackground() {
console.info("[EntryAbility] onBackground");
this.stopTimer();
this.saveData();
}
onWindowStageDestroy() {
console.info("[EntryAbility] onWindowStageDestroy");
this.releaseWindowResources();
}
onDestroy() {
console.info("[EntryAbility] onDestroy");
this.releaseResources();
this.unsubscribeAll();
}
async checkLoginStatus() {
const token = await storage.get("token");
if (token) {
AppStorage.SetOrCreate("token", token);
}
}
startTimer() {
}
stopTimer() {
}
refreshData() {
}
saveData() {
}
releaseWindowResources() {
}
releaseResources() {
}
unsubscribeAll() {
}
}
📖 三、页面生命周期
3.1 生命周期钩子
| 钩子函数 |
调用时机 |
用途 |
onPageShow() |
页面显示时 |
启动动画、刷新数据 |
onPageHide() |
页面隐藏时 |
停止动画、暂停任务 |
onBackPress() |
用户按返回键时 |
拦截返回、显示确认对话框 |
3.2 生命周期流程
页面跳转流程
页面 A(当前页面)
↓
(跳转到页面 B)
↓
页面 A: onPageHide() // 页面 A 隐藏
↓
页面 B: aboutToAppear() // 页面 B 组件即将出现
↓
页面 B: onPageShow() // 页面 B 显示
↓
(用户按返回键)
↓
页面 B: onBackPress() // 返回拦截(可选)
↓
页面 B: onPageHide() // 页面 B 隐藏
↓
页面 B: aboutToDisappear() // 页面 B 组件即将消失
↓
页面 A: onPageShow() // 页面 A 重新显示
3.3 代码示例
基本用法
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
private timer: number = -1
onPageShow() {
console.info('[Index] onPageShow - 页面显示')
this.timer = setInterval(() => {
console.info('定时任务执行')
}, 1000)
this.loadData()
this.startAnimation()
}
onPageHide() {
console.info('[Index] onPageHide - 页面隐藏')
if (this.timer !== -1) {
clearInterval(this.timer)
this.timer = -1
}
this.stopAnimation()
}
onBackPress(): boolean | void {
console.info('[Index] onBackPress - 按下返回键')
AlertDialog.show({
title: '提示',
message: '确定要退出应用吗?',
primaryButton: {
value: '确定',
action: () => {
this.context.terminateSelf()
}
},
secondaryButton: {
value: '取消',
action: () => {
console.info('用户取消退出')
}
}
})
return true
}
build() {
Column() {
Text(this.message)
.fontSize(24)
}
.width('100%')
.height('100%')
}
async loadData() {
}
startAnimation() {
}
stopAnimation() {
}
}
onBackPress 拦截返回
@Entry
@Component
struct FormPage {
@State formData: string = ''
@State isModified: boolean = false
onBackPress(): boolean | void {
if (this.isModified) {
AlertDialog.show({
title: '提示',
message: '表单尚未保存,确定要离开吗?',
primaryButton: {
value: '保存并离开',
action: () => {
this.saveForm().then(() => {
router.back()
})
}
},
secondaryButton: {
value: '不保存',
action: () => {
router.back()
}
}
})
return true
}
return false
}
build() {
Column() {
TextInput({ text: this.formData })
.onChange((value: string) => {
this.formData = value
this.isModified = true
})
Button('保存')
.onClick(() => {
this.saveForm()
})
}
}
async saveForm() {
console.info('保存表单')
this.isModified = false
}
}
📖 四、组件生命周期
4.1 生命周期钩子
| 钩子函数 |
调用时机 |
用途 |
aboutToAppear() |
组件即将出现 |
初始化数据、订阅事件 |
aboutToDisappear() |
组件即将消失 |
清理资源、取消订阅 |
aboutToReuse() |
组件即将复用(@Reusable) |
重置组件状态 |
4.2 生命周期流程
组件创建到销毁
组件创建
↓
constructor() // 构造函数(隐式)
↓
aboutToAppear() // 组件即将出现
↓
build() // 构建 UI(首次)
↓
组件显示
↓
(状态变化)
↓
build() // 重新构建 UI
↓
(组件销毁)
↓
aboutToDisappear() // 组件即将消失
↓
组件销毁
4.3 代码示例
基本用法
@Component
struct CounterComponent {
@State count: number = 0
private timer: number = -1
aboutToAppear() {
console.info('[CounterComponent] aboutToAppear - 组件即将出现')
this.loadInitialData()
this.timer = setInterval(() => {
this.count++
}, 1000)
eventHub.on('counterReset', this.resetCounter)
}
aboutToDisappear() {
console.info('[CounterComponent] aboutToDisappear - 组件即将消失')
if (this.timer !== -1) {
clearInterval(this.timer)
this.timer = -1
}
eventHub.off('counterReset', this.resetCounter)
this.cleanup()
}
build() {
Column() {
Text(`计数: ${this.count}`)
.fontSize(24)
Button('重置')
.onClick(() => {
this.count = 0
})
}
}
loadInitialData() {
console.info('加载初始数据')
}
resetCounter = () => {
this.count = 0
}
cleanup() {
console.info('清理资源')
}
}
父子组件生命周期
@Entry
@Component
struct ParentComponent {
@State showChild: boolean = true
aboutToAppear() {
console.info('[ParentComponent] aboutToAppear')
}
aboutToDisappear() {
console.info('[ParentComponent] aboutToDisappear')
}
build() {
Column() {
Text('父组件')
.fontSize(24)
Button(this.showChild ? '隐藏子组件' : '显示子组件')
.onClick(() => {
this.showChild = !this.showChild
})
if (this.showChild) {
ChildComponent()
}
}
}
}
@Component
struct ChildComponent {
aboutToAppear() {
console.info('[ChildComponent] aboutToAppear')
}
aboutToDisappear() {
console.info('[ChildComponent] aboutToDisappear')
}
build() {
Text('子组件')
.fontSize(20)
.fontColor(Color.Blue)
}
}
执行顺序:
显示子组件:
ParentComponent: aboutToAppear
ParentComponent: build
ChildComponent: aboutToAppear
ChildComponent: build
隐藏子组件:
ChildComponent: aboutToDisappear
ParentComponent: build(重新构建)
@Reusable 组件复用
@Reusable
@Component
struct ReusableCard {
@State title: string = ''
@State content: string = ''
aboutToAppear() {
console.info('[ReusableCard] aboutToAppear')
}
aboutToReuse(params: Record<string, Object>) {
console.info('[ReusableCard] aboutToReuse')
this.title = params.title as string
this.content = params.content as string
}
aboutToDisappear() {
console.info('[ReusableCard] aboutToDisappear')
}
build() {
Column() {
Text(this.title)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text(this.content)
.fontSize(16)
}
.padding(15)
.backgroundColor('#F5F5F5')
.borderRadius(8)
}
}
@Entry
@Component
struct CardList {
@State cardData: CardItem[] = [
{ id: 1, title: '卡片 1', content: '内容 1' },
{ id: 2, title: '卡片 2', content: '内容 2' },
{ id: 3, title: '卡片 3', content: '内容 3' }
]
build() {
List() {
ForEach(this.cardData, (item: CardItem) => {
ListItem() {
ReusableCard({
title: item.title,
content: item.content
})
}
}, (item: CardItem) => item.id.toString())
}
}
}
class CardItem {
id: number
title: string
content: string
}
📊 五、生命周期执行顺序
5.1 应用启动完整流程
1. UIAbility: onCreate() // Ability 创建
2. UIAbility: onWindowStageCreate() // 窗口舞台创建
3. 页面组件: aboutToAppear() // 页面组件即将出现
4. 子组件: aboutToAppear() // 子组件即将出现(从上到下)
5. 页面组件: build() // 构建 UI
6. 子组件: build() // 构建子组件 UI
7. 页面: onPageShow() // 页面显示
8. UIAbility: onForeground() // 进入前台
5.2 页面跳转完整流程
从页面 A 跳转到页面 B:
1. 页面 A: onPageHide() // 页面 A 隐藏
2. 页面 B: aboutToAppear() // 页面 B 组件即将出现
3. 页面 B 子组件: aboutToAppear() // 子组件即将出现
4. 页面 B: build() // 构建 UI
5. 页面 B: onPageShow() // 页面 B 显示
从页面 B 返回到页面 A:
1. 页面 B: onBackPress() // 返回拦截(可选)
2. 页面 B: onPageHide() // 页面 B 隐藏
3. 页面 B: aboutToDisappear() // 页面 B 组件即将消失
4. 页面 B 子组件: aboutToDisappear() // 子组件即将消失
5. 页面 A: onPageShow() // 页面 A 重新显示
5.3 应用后台/前台切换
进入后台:
1. 页面: onPageHide() // 页面隐藏
2. UIAbility: onBackground() // 进入后台
重新进入前台:
1. UIAbility: onForeground() // 进入前台
2. 页面: onPageShow() // 页面显示
5.4 应用销毁流程
1. 页面: onPageHide() // 页面隐藏
2. 页面组件: aboutToDisappear() // 页面组件即将消失
3. 子组件: aboutToDisappear() // 子组件即将消失(从下到上)
4. UIAbility: onBackground() // 进入后台
5. UIAbility: onWindowStageDestroy() // 窗口舞台销毁
6. UIAbility: onDestroy() // Ability 销毁
🎯 六、实际应用场景
6.1 场景 1:数据加载
@Entry
@Component
struct UserProfile {
@State userInfo: UserInfo | null = null
@State isLoading: boolean = true
aboutToAppear() {
console.info('[UserProfile] aboutToAppear')
this.loadUserInfo()
}
onPageShow() {
console.info('[UserProfile] onPageShow')
this.refreshUserInfo()
}
build() {
Column() {
if (this.isLoading) {
LoadingProgress()
.width(50)
.height(50)
} else if (this.userInfo) {
Text(`姓名: ${this.userInfo.name}`)
Text(`年龄: ${this.userInfo.age}`)
} else {
Text('加载失败')
}
}
}
async loadUserInfo() {
try {
this.isLoading = true
const userInfo = await UserService.getUserInfo()
this.userInfo = userInfo
} catch (error) {
console.error('加载用户信息失败:', error.message)
} finally {
this.isLoading = false
}
}
async refreshUserInfo() {
try {
const userInfo = await UserService.getUserInfo()
this.userInfo = userInfo
} catch (error) {
console.error('刷新用户信息失败:', error.message)
}
}
}
class UserInfo {
name: string
age: number
}
class UserService {
static async getUserInfo(): Promise<UserInfo> {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ name: '张三', age: 25 })
}, 1000)
})
}
}
6.2 场景 2:视频播放器
@Component
struct VideoPlayer {
@State isPlaying: boolean = false
private videoController: VideoController = new VideoController()
aboutToAppear() {
console.info('[VideoPlayer] aboutToAppear')
this.initPlayer()
}
onPageShow() {
console.info('[VideoPlayer] onPageShow')
if (this.isPlaying) {
this.videoController.start()
}
}
onPageHide() {
console.info('[VideoPlayer] onPageHide')
if (this.isPlaying) {
this.videoController.pause()
}
}
aboutToDisappear() {
console.info('[VideoPlayer] aboutToDisappear')
this.videoController.stop()
this.releasePlayer()
}
build() {
Column() {
Video({
src: 'https://example.com/video.mp4',
controller: this.videoController
})
.width('100%')
.height(300)
Row() {
Button(this.isPlaying ? '暂停' : '播放')
.onClick(() => {
if (this.isPlaying) {
this.videoController.pause()
} else {
this.videoController.start()
}
this.isPlaying = !this.isPlaying
})
}
}
}
initPlayer() {
console.info('初始化播放器')
}
releasePlayer() {
console.info('释放播放器资源')
}
}
6.3 场景 3:定时器管理
@Component
struct CountdownTimer {
@State countdown: number = 60
private timer: number = -1
aboutToAppear() {
console.info('[CountdownTimer] aboutToAppear')
this.startCountdown()
}
aboutToDisappear() {
console.info('[CountdownTimer] aboutToDisappear')
this.stopCountdown()
}
build() {
Column() {
Text(`倒计时: ${this.countdown}秒`)
.fontSize(24)
if (this.countdown === 0) {
Text('时间到!')
.fontSize(20)
.fontColor(Color.Red)
}
}
}
startCountdown() {
this.timer = setInterval(() => {
if (this.countdown > 0) {
this.countdown--
} else {
this.stopCountdown()
}
}, 1000)
}
stopCountdown() {
if (this.timer !== -1) {
clearInterval(this.timer)
this.timer = -1
}
}
}
6.4 场景 4:事件订阅
class EventBus {
private static instance: EventBus = new EventBus()
private events: Map<string, Function[]> = new Map()
static getInstance(): EventBus {
return this.instance
}
on(event: string, callback: Function) {
if (!this.events.has(event)) {
this.events.set(event, [])
}
this.events.get(event)!.push(callback)
}
off(event: string, callback: Function) {
const callbacks = this.events.get(event)
if (callbacks) {
const index = callbacks.indexOf(callback)
if (index > -1) {
callbacks.splice(index, 1)
}
}
}
emit(event: string, data?: any) {
const callbacks = this.events.get(event)
if (callbacks) {
callbacks.forEach(callback => callback(data))
}
}
}
@Component
struct NotificationListener {
@State notifications: string[] = []
private eventBus = EventBus.getInstance()
aboutToAppear() {
console.info('[NotificationListener] aboutToAppear')
this.eventBus.on('newNotification', this.handleNotification)
}
aboutToDisappear() {
console.info('[NotificationListener] aboutToDisappear')
this.eventBus.off('newNotification', this.handleNotification)
}
build() {
Column() {
Text('通知列表')
.fontSize(24)
List() {
ForEach(this.notifications, (notification: string, index: number) => {
ListItem() {
Text(`${index + 1}. ${notification}`)
}
}, (notification: string, index: number) => index.toString())
}
}
}
handleNotification = (notification: string) => {
console.info('收到新通知:', notification)
this.notifications.push(notification)
}
}
@Entry
@Component
struct NotificationSender {
private eventBus = EventBus.getInstance()
build() {
Column() {
Button('发送通知')
.onClick(() => {
this.eventBus.emit('newNotification', `通知 ${Date.now()}`)
})
Divider().margin(20)
NotificationListener()
}
}
}
⚠️ 七、注意事项和最佳实践
7.1 资源管理
✅ 正确做法
@Component
struct ResourceComponent {
private timer: number = -1
private subscription: Subscription | null = null
aboutToAppear() {
this.timer = setInterval(() => {
console.info('定时任务')
}, 1000)
this.subscription = eventBus.subscribe('event', this.handleEvent)
}
aboutToDisappear() {
if (this.timer !== -1) {
clearInterval(this.timer)
this.timer = -1
}
if (this.subscription) {
this.subscription.unsubscribe()
this.subscription = null
}
}
build() {
Text('资源管理示例')
}
handleEvent = () => {
}
}
❌ 错误做法
@Component
struct BadResourceComponent {
private timer: number = -1
aboutToAppear() {
this.timer = setInterval(() => {
console.info('定时任务')
}, 1000)
}
}
7.2 异步操作
✅ 正确做法
@Component
struct AsyncComponent {
@State data: string = ''
private abortController: AbortController | null = null
aboutToAppear() {
this.abortController = new AbortController()
this.loadData()
}
aboutToDisappear() {
if (this.abortController) {
this.abortController.abort()
this.abortController = null
}
}
async loadData() {
try {
const response = await fetch('https://api.example.com/data', {
signal: this.abortController?.signal
})
const data = await response.json()
this.data = data
} catch (error) {
if (error.name === 'AbortError') {
console.info('请求已取消')
} else {
console.error('加载数据失败:', error.message)
}
}
}
build() {
Text(this.data)
}
}
7.3 状态初始化
✅ 正确做法
@Component
struct StateComponent {
@State count: number = 0
aboutToAppear() {
this.loadInitialData()
}
build() {
Text(`计数: ${this.count}`)
}
async loadInitialData() {
const initialValue = await storage.get('count')
if (initialValue !== null) {
this.count = initialValue
}
}
}
❌ 错误做法
@Component
struct BadStateComponent {
@State count: number
aboutToAppear() {
this.count = 0
}
build() {
Text(`计数: ${this.count}`)
}
}
7.4 父子组件通信
✅ 正确做法
@Entry
@Component
struct ParentComponent {
@State parentData: string = '父组件数据'
aboutToAppear() {
console.info('[ParentComponent] aboutToAppear')
}
build() {
Column() {
ChildComponent({ childData: this.parentData })
}
}
}
@Component
struct ChildComponent {
@Prop childData: string
aboutToAppear() {
console.info('[ChildComponent] aboutToAppear')
console.info('接收到的数据:', this.childData)
}
build() {
Text(this.childData)
}
}
7.5 生命周期中的性能优化
@Component
struct OptimizedComponent {
@State data: DataItem[] = []
private cache: Map<string, any> = new Map()
aboutToAppear() {
if (this.cache.has('data')) {
this.data = this.cache.get('data')
} else {
this.loadData()
}
}
aboutToDisappear() {
this.cache.set('data', this.data)
}
build() {
List() {
ForEach(this.data, (item: DataItem) => {
ListItem() {
Text(item.name)
}
}, (item: DataItem) => item.id)
}
}
async loadData() {
}
}
class DataItem {
id: string
name: string
}
🎓 八、快速参考
8.1 生命周期钩子速查表
| 层级 |
钩子函数 |
调用时机 |
常见用途 |
| UIAbility |
onCreate() |
Ability 创建 |
全局初始化 |
| UIAbility |
onWindowStageCreate() |
窗口舞台创建 |
加载首页 |
| UIAbility |
onForeground() |
进入前台 |
恢复任务 |
| UIAbility |
onBackground() |
进入后台 |
暂停任务、保存数据 |
| UIAbility |
onDestroy() |
Ability 销毁 |
释放资源 |
| 页面 |
onPageShow() |
页面显示 |
刷新数据、启动动画 |
| 页面 |
onPageHide() |
页面隐藏 |
停止动画、暂停任务 |
| 页面 |
onBackPress() |
按下返回键 |
拦截返回 |
| 组件 |
aboutToAppear() |
组件即将出现 |
初始化、订阅事件 |
| 组件 |
aboutToDisappear() |
组件即将消失 |
清理资源、取消订阅 |
| 组件 |
aboutToReuse() |
组件复用 |
更新状态 |
8.2 常见场景速查
| 场景 |
推荐钩子 |
说明 |
| 加载数据 |
aboutToAppear() |
组件首次创建时加载 |
| 刷新数据 |
onPageShow() |
每次页面显示时刷新 |
| 启动定时器 |
aboutToAppear() |
组件创建时启动 |
| 停止定时器 |
aboutToDisappear() |
组件销毁时停止 |
| 订阅事件 |
aboutToAppear() |
组件创建时订阅 |
| 取消订阅 |
aboutToDisappear() |
组件销毁时取消 |
| 播放视频 |
onPageShow() |
页面显示时播放 |
| 暂停视频 |
onPageHide() |
页面隐藏时暂停 |
| 保存数据 |
onBackground() |
进入后台时保存 |
| 拦截返回 |
onBackPress() |
显示确认对话框 |
| 释放资源 |
aboutToDisappear() |
组件销毁时释放 |
8.3 常见错误速查
| 错误 |
原因 |
解决方案 |
| 内存泄漏 |
未清理定时器 |
在 aboutToDisappear() 中清理 |
| 异步请求未取消 |
组件销毁后仍在请求 |
使用 AbortController |
| 状态未初始化 |
忘记初始化 @State |
在声明时初始化 |
| 事件监听未移除 |
忘记取消订阅 |
在 aboutToDisappear() 中取消 |
| 数据未刷新 |
未在 onPageShow() 刷新 |
添加刷新逻辑 |
📌 九、总结
核心要点
- 三个层级:UIAbility、页面、组件,各有不同的生命周期
- 成对出现:
aboutToAppear() 对应 aboutToDisappear()
- 资源管理:在
aboutToAppear() 中创建,在 aboutToDisappear() 中释放
- 数据刷新:使用
onPageShow() 刷新页面数据
- 返回拦截:使用
onBackPress() 拦截返回事件
最佳实践
- ✅ 总是清理资源:定时器、订阅、网络请求
- ✅ 合理使用缓存:避免重复加载数据
- ✅ 异步操作要可取消:使用 AbortController
- ✅ 状态要初始化:在声明时初始化 @State
- ✅ 生命周期要配对:创建和销毁成对出现
记忆口诀
Ability 生命周期,六个钩子记心里
onCreate 初始化,onDestroy 来清理
页面显示 onPageShow,隐藏调用 onPageHide
组件即将 aboutToAppear,消失调用 aboutToDisappear
资源创建要成对,清理释放不能忘
数据刷新用 PageShow,返回拦截 BackPress
所有评论(0)