頁面和自定義組件生命周期
在開始之前,先明確自定義組件和頁面的關(guān)系:
- 自定義組件:@Component裝飾的UI單元,可以組合多個(gè)系統(tǒng)組件實(shí)現(xiàn)UI的復(fù)用。
- 頁面:即應(yīng)用的UI頁面??梢杂梢粋€(gè)或者多個(gè)自定義組件組成,@Entry裝飾的自定義組件為頁面的入口組件,即頁面的根節(jié)點(diǎn),一個(gè)頁面有且僅能有一個(gè)@Entry。只有被@Entry裝飾的組件才可以調(diào)用頁面的生命周期。
頁面生命周期,即被@Entry裝飾的組件生命周期,提供以下生命周期接口:
- onPageShow:頁面每次顯示時(shí)觸發(fā)。
- onPageHide:頁面每次隱藏時(shí)觸發(fā)一次。
- onBackPress:當(dāng)用戶點(diǎn)擊返回按鈕時(shí)觸發(fā)。
組件生命周期,即一般用@Component裝飾的自定義組件的生命周期,提供以下生命周期接口:
- aboutToAppear:組件即將出現(xiàn)時(shí)回調(diào)該接口,具體時(shí)機(jī)為在創(chuàng)建自定義組件的新實(shí)例后,在執(zhí)行其build()函數(shù)之前執(zhí)行。
- aboutToDisappear:在自定義組件即將析構(gòu)銷毀時(shí)執(zhí)行。
生命周期流程如下圖所示,下圖展示的是被@Entry裝飾的組件(首頁)生命周期。
?
自定義組件的創(chuàng)建和渲染流程
- 自定義組件的創(chuàng)建:自定義組件的實(shí)例由ArkUI框架創(chuàng)建。
- 初始化自定義組件的成員變量:通過本地默認(rèn)值或者構(gòu)造方法傳遞參數(shù)來初始化自定義組件的成員變量,初始化順序?yàn)槌蓡T變量的定義順序。
- 如果開發(fā)者定義了aboutToAppear,則執(zhí)行aboutToAppear方法。
- 在首次渲染的時(shí)候,執(zhí)行build方法渲染系統(tǒng)組件,如果有自定義子組件,則創(chuàng)建自定義組件的實(shí)例。在執(zhí)行build()函數(shù)的過程中,框架會(huì)觀察每個(gè)狀態(tài)變量的讀取狀態(tài),將保存兩個(gè)map:
- 狀態(tài)變量 -> UI組件(包括ForEach和if)。
- UI組件 -> 此組件的更新函數(shù),即一個(gè)lambda方法,作為build()函數(shù)的子集,創(chuàng)建對應(yīng)的UI組件并執(zhí)行其屬性方法,示意如下。
build() {
...
this.observeComponentCreation(() => {
Button.create();
})
this.observeComponentCreation(() => {
Text.create();
})
...
}
當(dāng)應(yīng)用在后臺啟動(dòng)時(shí),此時(shí)應(yīng)用進(jìn)程并沒有銷毀,所以僅需要執(zhí)行onPageShow。
自定義組件重新渲染
當(dāng)事件句柄被觸發(fā)(比如設(shè)置了點(diǎn)擊事件,即觸發(fā)點(diǎn)擊事件)改變了狀態(tài)變量時(shí),或者LocalStorage / AppStorage中的屬性更改,并導(dǎo)致綁定的狀態(tài)變量更改其值時(shí):
- 框架觀察到了變化,將啟動(dòng)重新渲染。
- 根據(jù)框架持有的兩個(gè)map(自定義組件的創(chuàng)建和渲染流程中第4步),框架可以知道該狀態(tài)變量管理了哪些UI組件,以及這些UI組件對應(yīng)的更新函數(shù)。執(zhí)行這些UI組件的更新函數(shù),實(shí)現(xiàn)最小化更新。
自定義組件的刪除
如果if組件的分支改變,或者ForEach循環(huán)渲染中數(shù)組的個(gè)數(shù)改變,組件將被刪除:
- 在刪除組件之前,將調(diào)用其aboutToDisappear生命周期函數(shù),標(biāo)記著該節(jié)點(diǎn)將要被銷毀。ArkUI的節(jié)點(diǎn)刪除機(jī)制是:后端節(jié)點(diǎn)直接從組件樹上摘下,后端節(jié)點(diǎn)被銷毀,對前端節(jié)點(diǎn)解引用,當(dāng)前端節(jié)點(diǎn)已經(jīng)沒有引用時(shí),將被JS虛擬機(jī)垃圾回收。
- 自定義組件和它的變量將被刪除,如果其有同步的變量,比如@Link、@Prop、@StorageLink,將從同步源上取消注冊。
不建議在生命周期aboutToDisappear內(nèi)使用async await,如果在生命周期的aboutToDisappear使用異步操作(Promise或者回調(diào)方法),自定義組件將被保留在Promise的閉包中,直到回調(diào)方法被執(zhí)行完,這個(gè)行為阻止了自定義組件的垃圾回收。
以下示例展示了生命周期的調(diào)用時(shí)機(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('create or 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頁面包含兩個(gè)自定義組件,一個(gè)是被@Entry裝飾的MyComponent,也是頁面的入口組件,即頁面的根節(jié)點(diǎn);一個(gè)是Child,是MyComponent的子組件。只有@Entry裝飾的節(jié)點(diǎn)才可以生效頁面的生命周期方法,所以MyComponent中聲明了當(dāng)前Index頁面的頁面生命周期函數(shù)。MyComponent和其子組件Child也同時(shí)也聲明了組件的生命周期函數(shù)。
- 應(yīng)用冷啟動(dò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組件,會(huì)執(zhí)行Child aboutToDisappear方法。
- 點(diǎn)擊“push to next page”,調(diào)用router.pushUrl接口,跳轉(zhuǎn)到另外一個(gè)頁面,當(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。最小化應(yīng)用或者應(yīng)用進(jìn)入后臺,觸發(fā)Index onPageHide。這兩個(gè)狀態(tài)下應(yīng)用都沒有被銷毀,所以并不會(huì)執(zhí)行組件的aboutToDisappear 。應(yīng)用回到前臺,執(zhí)行Index onPageShow。
- 退出應(yīng)用,執(zhí)行Index onPageHide --> MyComponent aboutToDisappear --> Child aboutToDisappear。
@Builder裝飾器:自定義構(gòu)建函數(shù)
前面章節(jié)介紹了如何創(chuàng)建一個(gè)自定義組件。該自定義組件內(nèi)部UI結(jié)構(gòu)固定,僅與使用方進(jìn)行數(shù)據(jù)傳遞。ArkUI還提供了一種更輕量的UI元素復(fù)用機(jī)制@Builder,@Builder所裝飾的函數(shù)遵循build()函數(shù)語法規(guī)則,開發(fā)者可以將重復(fù)使用的UI元素抽象成一個(gè)方法,在build方法里調(diào)用。
為了簡化語言,將@Builder裝飾的函數(shù)也稱為“自定義構(gòu)建函數(shù)”。
裝飾器使用說明
自定義組件內(nèi)自定義構(gòu)建函數(shù)
定義的語法:
@Builder myBuilderFunction({ ... })
使用方法:
this.myBuilderFunction({ ... })
- 允許在自定義組件內(nèi)定義一個(gè)或多個(gè)自定義構(gòu)建函數(shù),該函數(shù)被認(rèn)為是該組件的私有、特殊類型的成員函數(shù)。
- 自定義構(gòu)建函數(shù)可以在所屬組件的build方法和其他自定義構(gòu)建函數(shù)中調(diào)用,但不允許在組件外調(diào)用。
- 在自定義函數(shù)體中,this指代當(dāng)前所屬組件,組件的狀態(tài)變量可以在自定義構(gòu)建函數(shù)內(nèi)訪問。建議通過this訪問自定義組件的狀態(tài)變量而不是參數(shù)傳遞。
全局自定義構(gòu)建函數(shù)
定義的語法:
@Builder function MyGlobalBuilderFunction({ ... })
使用方法:
MyGlobalBuilderFunction()
- 全局的自定義構(gòu)建函數(shù)可以被整個(gè)應(yīng)用獲取,不允許使用this和bind方法。
- 如果不涉及組件狀態(tài)變化,建議使用全局的自定義構(gòu)建方法。
參數(shù)傳遞規(guī)則
自定義構(gòu)建函數(shù)的參數(shù)傳遞有按值傳遞和按引用傳遞兩種,均需遵守以下規(guī)則:
- 參數(shù)的類型必須與參數(shù)聲明的類型一致,不允許undefined、null和返回undefined、null的表達(dá)式。
- 在自定義構(gòu)建函數(shù)內(nèi)部,不允許改變參數(shù)值。如果需要改變參數(shù)值,且同步回調(diào)用點(diǎn),建議使用@Link。
- @Builder內(nèi)UI語法遵循UI語法規(guī)則。
按引用傳遞參數(shù)
按引用傳遞參數(shù)時(shí),傳遞的參數(shù)可為狀態(tài)變量,且狀態(tài)變量的改變會(huì)引起@Builder方法內(nèi)的UI刷新。ArkUI提供$$作為按引用傳遞參數(shù)的范式。文章來源:http://www.zghlxwxcb.cn/news/detail-579767.html
ABuilder( $$ : { paramA1: string, paramB1 : string } );
@Builder function ABuilder($$: { paramA1: string }) {
Row() {
Text(`UseStateVarByReference: ${$$.paramA1} `)
}
}
@Entry
@Component
struct Parent {
@State label: string = 'Hello';
build() {
Column() {
// 在Parent組件中調(diào)用ABuilder的時(shí)候,將this.label引用傳遞給ABuilder
ABuilder({ paramA1: this.label })
Button('Click me').onClick(() => {
// 點(diǎn)擊“Click me”后,UI從“Hello”刷新為“ArkUI”
this.label = 'ArkUI';
})
}
}
}
按值傳遞參數(shù)
調(diào)用@Builder裝飾的函數(shù)默認(rèn)按值傳遞。當(dāng)傳遞的參數(shù)為狀態(tài)變量時(shí),狀態(tài)變量的改變不會(huì)引起@Builder方法內(nèi)的UI刷新。所以當(dāng)使用狀態(tài)變量的時(shí)候,推薦使用按引用傳遞。文章來源地址http://www.zghlxwxcb.cn/news/detail-579767.html
@Builder function ABuilder(paramA1: string) {
Row() {
Text(`UseStateVarByValue: ${paramA1} `)
}
}
@Entry
@Component
struct Parent {
label: string = 'Hello';
build() {
Column() {
ABuilder(this.label)
}
}
}
到了這里,關(guān)于HarmonyOS學(xué)習(xí)路之方舟開發(fā)框架—學(xué)習(xí)ArkTS語言(基本語法 三)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!