自定義組件和頁面的關(guān)系:
- 自定義組件:@Component裝飾的UI單元,可以組合多個系統(tǒng)組件實現(xiàn)UI的復(fù)用,可以調(diào)用組件的生命周期柳恐。
- 頁面:即應(yīng)用的UI頁面∪柔#可以由一個或者多個自定義組件組成乐设,@Entry裝飾的自定義組件為頁面的入口組件,即頁面的根節(jié)點(diǎn)绎巨,一個頁面有且僅能有一個@Entry近尚。只有被@Entry裝飾的組件才可以調(diào)用頁面的生命周期。
頁面生命周期场勤,即被@Entry裝飾的組件生命周期戈锻,提供以下生命周期接口:
- onPageShow:頁面每次顯示時觸發(fā)一次,包括路由過程和媳、應(yīng)用進(jìn)入前臺等場景格遭。
- onPageHide:頁面每次隱藏時觸發(fā)一次,包括路由過程留瞳、應(yīng)用進(jìn)入后臺等場景拒迅。
- onBackPress:當(dāng)用戶點(diǎn)擊返回按鈕時觸發(fā)。
組件生命周期,即一般用@Component裝飾的自定義組件的生命周期坪它,提供以下生命周期接口:
- aboutToAppear:組件即將出現(xiàn)時回調(diào)該接口骤竹,具體時機(jī)為在創(chuàng)建自定義組件的新實例后,在執(zhí)行其build()函數(shù)之前執(zhí)行往毡。
- aboutToDisappear:在自定義組件析構(gòu)銷毀之前執(zhí)行蒙揣。不允許在aboutToDisappear函數(shù)中改變狀態(tài)變量,特別是@Link變量的修改可能會導(dǎo)致應(yīng)用程序行為不穩(wěn)定开瞭。
生命周期流程如下圖所示懒震,下圖展示的是被@Entry裝飾的組件(首頁)生命周期。
根據(jù)上面的流程圖嗤详,我們從自定義組件的初始創(chuàng)建个扰、重新渲染和刪除來詳細(xì)解釋。
自定義組件的創(chuàng)建和渲染流程
- 自定義組件的創(chuàng)建:自定義組件的實例由ArkUI框架創(chuàng)建葱色。
- 初始化自定義組件的成員變量:通過本地默認(rèn)值或者構(gòu)造方法傳遞參數(shù)來初始化自定義組件的成員變量递宅,初始化順序為成員變量的定義順序。
- 如果開發(fā)者定義了aboutToAppear苍狰,則執(zhí)行aboutToAppear方法办龄。
- 在首次渲染的時候,執(zhí)行build方法渲染系統(tǒng)組件淋昭,如果子組件為自定義組件俐填,則創(chuàng)建自定義組件的實例。在執(zhí)行build()函數(shù)的過程中翔忽,框架會觀察每個狀態(tài)變量的讀取狀態(tài)英融,將保存兩個map:
- 狀態(tài)變量 -> UI組件(包括ForEach和if)。
- UI組件 -> 此組件的更新函數(shù)歇式,即一個lambda方法驶悟,作為build()函數(shù)的子集,創(chuàng)建對應(yīng)的UI組件并執(zhí)行其屬性方法材失,示意如下痕鳍。
build() {
...
this.observeComponentCreation(() => {
Button.create();
})
this.observeComponentCreation(() => {
Text.create();
})
...
}
當(dāng)應(yīng)用在后臺啟動時,此時應(yīng)用進(jìn)程并沒有銷毀豺憔,所以僅需要執(zhí)行onPageShow额获。
自定義組件重新渲染
當(dāng)事件句柄被觸發(fā)(比如設(shè)置了點(diǎn)擊事件够庙,即觸發(fā)點(diǎn)擊事件)改變了狀態(tài)變量時恭应,或者LocalStorage / AppStorage中的屬性更改,并導(dǎo)致綁定的狀態(tài)變量更改其值時:
- 框架觀察到了變化耘眨,將啟動重新渲染昼榛。
- 根據(jù)框架持有的兩個map(自定義組件的創(chuàng)建和渲染流程中第4步),框架可以知道該狀態(tài)變量管理了哪些UI組件,以及這些UI組件對應(yīng)的更新函數(shù)胆屿。執(zhí)行這些UI組件的更新函數(shù)奥喻,實現(xiàn)最小化更新。
自定義組件的刪除
如果if組件的分支改變非迹,或者ForEach循環(huán)渲染中數(shù)組的個數(shù)改變环鲤,組件將被刪除:
- 在刪除組件之前,將調(diào)用其aboutToDisappear生命周期函數(shù)憎兽,標(biāo)記著該節(jié)點(diǎn)將要被銷毀冷离。ArkUI的節(jié)點(diǎn)刪除機(jī)制是:后端節(jié)點(diǎn)直接從組件樹上摘下,后端節(jié)點(diǎn)被銷毀纯命,對前端節(jié)點(diǎn)解引用西剥,前端節(jié)點(diǎn)已經(jīng)沒有引用時,將被JS虛擬機(jī)垃圾回收亿汞。
- 自定義組件和它的變量將被刪除瞭空,如果其有同步的變量,比如@Link疗我、@Prop咆畏、@StorageLink,將從同步源上取消注冊碍粥。
不建議在生命周期aboutToDisappear內(nèi)使用async await鳖眼,如果在生命周期的aboutToDisappear使用異步操作(Promise或者回調(diào)方法),自定義組件將被保留在Promise的閉包中嚼摩,直到回調(diào)方法被執(zhí)行完钦讳,這個行為阻止了自定義組件的垃圾回收。
以下示例展示了生命周期的調(diào)用時機(jī):
// Index.ets
import router from '@ohos.router';
@Entry
@Component
struct MyComponent {
@State showChild: boolean = true;
// 只有被@Entry裝飾的組件才可以調(diào)用頁面的生命周期
onPageShow() {
console.info('Index onPageShow');
}
// 只有被@Entry裝飾的組件才可以調(diào)用頁面的生命周期
onPageHide() {
console.info('Index onPageHide');
}
// 只有被@Entry裝飾的組件才可以調(diào)用頁面的生命周期
onBackPress() {
console.info('Index onBackPress');
}
// 組件生命周期
aboutToAppear() {
console.info('MyComponent aboutToAppear');
}
// 組件生命周期
aboutToDisappear() {
console.info('MyComponent aboutToDisappear');
}
build() {
Column() {
// this.showChild為true枕面,創(chuàng)建Child子組件愿卒,執(zhí)行Child aboutToAppear
if (this.showChild) {
Child()
}
// this.showChild為false,刪除Child子組件潮秘,執(zhí)行Child aboutToDisappear
Button('delete Child').onClick(() => {
this.showChild = false;
})
// push到Page2頁面琼开,執(zhí)行onPageHide
Button('push to next page')
.onClick(() => {
router.pushUrl({ url: 'pages/Page2' });
})
}
}
}
@Component
struct Child {
@State title: string = 'Hello World';
// 組件生命周期
aboutToDisappear() {
console.info('[lifeCycle] Child aboutToDisappear')
}
// 組件生命周期
aboutToAppear() {
console.info('[lifeCycle] Child aboutToAppear')
}
build() {
Text(this.title).fontSize(50).onClick(() => {
this.title = 'Hello ArkUI';
})
}
}
以上示例中,Index頁面包含兩個自定義組件枕荞,一個是被@Entry裝飾的MyComponent柜候,也是頁面的入口組件,即頁面的根節(jié)點(diǎn)躏精;一個是Child渣刷,是MyComponent的子組件。只有@Entry裝飾的節(jié)點(diǎn)才可以使頁面級別的生命周期方法生效矗烛,所以MyComponent中聲明了當(dāng)前Index頁面的頁面生命周期函數(shù)辅柴。MyComponent和其子組件Child也同時也聲明了組件的生命周期函數(shù)。
應(yīng)用冷啟動的初始化流程為:MyComponent aboutToAppear --> MyComponent build --> Child aboutToAppear --> Child build --> Child build執(zhí)行完畢 --> MyComponent build執(zhí)行完畢 --> Index onPageShow。
點(diǎn)擊“delete Child”碌嘀,if綁定的this.showChild變成false涣旨,刪除Child組件,會執(zhí)行Child aboutToDisappear方法股冗。
點(diǎn)擊“push to next page”霹陡,調(diào)用router.pushUrl接口,跳轉(zhuǎn)到另外一個頁面止状,當(dāng)前Index頁面隱藏穆律,執(zhí)行頁面生命周期Index onPageHide。此處調(diào)用的是router.pushUrl接口导俘,Index頁面被隱藏峦耘,并沒有銷毀,所以只調(diào)用onPageHide旅薄。跳轉(zhuǎn)到新頁面后辅髓,執(zhí)行初始化新頁面的生命周期的流程。
如果調(diào)用的是router.replaceUrl少梁,則當(dāng)前Index頁面被銷毀洛口,執(zhí)行的生命周期流程將變?yōu)椋篒ndex onPageHide --> MyComponent aboutToDisappear --> Child aboutToDisappear。上文已經(jīng)提到凯沪,組件的銷毀是從組件樹上直接摘下子樹第焰,所以先調(diào)用父組件的aboutToDisappear,再調(diào)用子組件的aboutToDisappear妨马,然后執(zhí)行初始化新頁面的生命周期流程挺举。
點(diǎn)擊返回按鈕,觸發(fā)頁面生命周期Index onBackPress烘跺,且觸發(fā)返回一個頁面后會導(dǎo)致當(dāng)前Index頁面被銷毀湘纵。
最小化應(yīng)用或者應(yīng)用進(jìn)入后臺,觸發(fā)Index onPageHide滤淳。當(dāng)前Index頁面沒有被銷毀梧喷,所以并不會執(zhí)行組件的aboutToDisappear。應(yīng)用回到前臺脖咐,執(zhí)行Index onPageShow铺敌。
退出應(yīng)用,執(zhí)行Index onPageHide --> MyComponent aboutToDisappear --> Child aboutToDisappear屁擅。