查企業(yè)網(wǎng)站網(wǎng)絡(luò)推廣途徑
一、創(chuàng)建自定義組件
在ArkUI中,UI顯示的內(nèi)容均為組件,由框架直接提供的稱為系統(tǒng)組件,由開發(fā)者定義的稱為自定義組件。在進(jìn)行?UI?界面開發(fā)時(shí),通常不是簡(jiǎn)單的將系統(tǒng)組件進(jìn)行組合使用,而是需要考慮代碼可復(fù)用性、業(yè)務(wù)邏輯與UI分離,后續(xù)版本演進(jìn)等因素。因此,將UI和部分業(yè)務(wù)邏輯封裝成自定義組件是不可或缺的能力。
自定義組件具有以下特點(diǎn):
- 可組合:允許開發(fā)者組合使用系統(tǒng)組件、及其屬性和方法。
- 可重用:自定義組件可以被其他組件重用,并作為不同的實(shí)例在不同的父組件或容器中使用。
- 數(shù)據(jù)驅(qū)動(dòng)UI更新:通過(guò)狀態(tài)變量的改變,來(lái)驅(qū)動(dòng)UI的刷新。
以下示例展示了自定義組件的基本用法。
@Component
struct?HelloComponent?{@State?message: string = 'Hello,?World!';build() {//?HelloComponent自定義組件組合系統(tǒng)組件Row和TextRow() {Text(this.message).onClick(() => {//?狀態(tài)變量message的改變驅(qū)動(dòng)UI刷新,UI從'Hello,?World!'刷新為'Hello,?ArkUI!'this.message?= 'Hello,?ArkUI!';})}}
}
HelloComponent可以在其他自定義組件中的build()函數(shù)中多次創(chuàng)建,實(shí)現(xiàn)自定義組件的重用。
class HelloComponentParam {
??message: string = ""
}@Entry
@Component
struct?ParentComponent?{
??param:?HelloComponentParam?= {
????message: 'Hello,?World!'}build() {Column() {Text('ArkUI?message')HelloComponent(param);Divider()HelloComponent(param);}}
}
要完全理解上面的示例,需要了解自定義組件的以下概念定義,本文將在后面的小節(jié)中介紹:
- 自定義組件的基本結(jié)構(gòu)
- 成員函數(shù)/變量
- 自定義組件的參數(shù)規(guī)定
- build()函數(shù)
- 自定義組件通用樣式
自定義組件的基本結(jié)構(gòu)
- struct:自定義組件基于struct實(shí)現(xiàn),struct?+?自定義組件名?+?{...}的組合構(gòu)成自定義組件,不能有繼承關(guān)系。對(duì)于struct的實(shí)例化,可以省略new。
說(shuō)明:自定義組件名、類名、函數(shù)名不能和系統(tǒng)組件名相同。
- @Component:@Component裝飾器僅能裝飾struct關(guān)鍵字聲明的數(shù)據(jù)結(jié)構(gòu)。struct被@Component裝飾后具備組件化的能力,需要實(shí)現(xiàn)build方法描述UI,一個(gè)struct只能被一個(gè)@Component裝飾。
說(shuō)明:從API?version?9開始,該裝飾器支持在ArkTS卡片中使用。
@Component
struct?MyComponent?{
}
- build()函數(shù):build()函數(shù)用于定義自定義組件的聲明式UI描述,自定義組件必須定義build()函數(shù)。
@Component
struct?MyComponent?{
??build()?{
??}
}
- @Entry:@Entry裝飾的自定義組件將作為UI頁(yè)面的入口。在單個(gè)UI頁(yè)面中,最多可以使用@Entry裝飾一個(gè)自定義組件。@Entry可以接受一個(gè)可選的LocalStorage的參數(shù)。
說(shuō)明:
從API?version?9開始,該裝飾器支持在ArkTS卡片中使用。
從API?version?10開始,@Entry可以接受一個(gè)可選的LocalStorage的參數(shù)或者一個(gè)可選的EntryOptions參數(shù)。
@Entry
@Component
struct?MyComponent?{
}
EntryOptions10+命名路由跳轉(zhuǎn)選項(xiàng)。
名稱 | 類型 | 必填 | 說(shuō)明 |
routeName | string | 否 | 表示作為命名路由頁(yè)面的名字。 |
storage | LocalStorage | 否 | 頁(yè)面級(jí)的UI狀態(tài)存儲(chǔ)。 |
@Entry({?routeName?: 'myPage' })
@Component
struct?MyComponent?{
}
- @Reusable:@Reusable裝飾的自定義組件具備可復(fù)用能力
說(shuō)明:
從API?version?10開始,該裝飾器支持在ArkTS卡片中使用。
@Reusable
@Component
struct?MyComponent?{
}
成員函數(shù)/變量
自定義組件除了必須要實(shí)現(xiàn)build()函數(shù)外,還可以實(shí)現(xiàn)其他成員函數(shù),成員函數(shù)具有以下約束:
- 不支持靜態(tài)函數(shù)。
- 成員函數(shù)的訪問(wèn)始終是私有的。
自定義組件可以包含成員變量,成員變量具有以下約束:
- 不支持靜態(tài)成員變量。
- 所有成員變量都是私有的,變量的訪問(wèn)規(guī)則與成員函數(shù)的訪問(wèn)規(guī)則相同。
- 自定義組件的成員變量本地初始化有些是可選的,有些是必選的。具體是否需要本地初始化,是否需要從父組件通過(guò)參數(shù)傳遞初始化子組件的成員變量,請(qǐng)參考狀態(tài)管理。
自定義組件的參數(shù)規(guī)定
從上文的示例中,我們已經(jīng)了解到,可以在build方法或者@Builder裝飾的函數(shù)里創(chuàng)建自定義組件,在創(chuàng)建的過(guò)程中,參數(shù)可以被提供給組件。
@Component
struct?MyComponent?{private?countDownFrom: number = 0;private?color:?Color?=?Color.Blue;build() {}
}@Entry
@Component
struct?ParentComponent?{private?someColor:?Color?=?Color.Pink;build() {Column() {//?創(chuàng)建MyComponent實(shí)例,并將創(chuàng)建MyComponent成員變量countDownFrom初始化為10,將成員變量color初始化為this.someColorMyComponent({?countDownFrom: 10,?color: this.someColor?})}}
}
build()函數(shù)
所有聲明在build()函數(shù)的語(yǔ)言,我們統(tǒng)稱為UI描述語(yǔ)言,UI描述語(yǔ)言需要遵循以下規(guī)則:
- @Entry裝飾的自定義組件,其build()函數(shù)下的根節(jié)點(diǎn)唯一且必要,且必須為容器組件,其中ForEach禁止作為根節(jié)點(diǎn)。?@Component裝飾的自定義組件,其build()函數(shù)下的根節(jié)點(diǎn)唯一且必要,可以為非容器組件,其中ForEach禁止作為根節(jié)點(diǎn)。
@Entry
@Component
struct?MyComponent?{build() {//?根節(jié)點(diǎn)唯一且必要,必須為容器組件Row() {ChildComponent() }}
}@Component
struct?ChildComponent?{build() {//?根節(jié)點(diǎn)唯一且必要,可為非容器組件Image('test.jpg')}
}
- 不允許聲明本地變量,反例如下。
build() {//?反例:不允許聲明本地變量let?a: number = 1;
}
- 不允許在UI描述里直接使用console.info,但允許在方法或者函數(shù)里使用,反例如下。
build()?{
??//?反例:不允許console.info
??console.info('print?debug?log');
}
- 不允許創(chuàng)建本地的作用域,反例如下。
build() {//?反例:不允許本地作用域{...}
}
- 不允許調(diào)用除了被@Builder裝飾以外的方法,允許系統(tǒng)組件的參數(shù)是TS方法的返回值。
@Component
struct?ParentComponent?{doSomeCalculations() {}calcTextValue(): string {return 'Hello?World';}@Builder doSomeRender() {Text(`Hello?World`)}build() {Column() {//?反例:不能調(diào)用沒有用@Builder裝飾的方法this.doSomeCalculations();//?正例:可以調(diào)用this.doSomeRender();//?正例:參數(shù)可以為調(diào)用TS方法的返回值Text(this.calcTextValue())}}
}
- 不允許使用switch語(yǔ)法,如果需要使用條件判斷,請(qǐng)使用if。反例如下。
build() {Column() {//?反例:不允許使用switch語(yǔ)法switch (expression) {case 1:Text('...')break;case 2:Image('...')break;default:Text('...')break;}}
}
- 不允許使用表達(dá)式,反例如下。
build() {Column() {//?反例:不允許使用表達(dá)式(this.aVar?> 10) ? Text('...') : Image('...')}
}
自定義組件通用樣式
自定義組件通過(guò)“.”鏈?zhǔn)秸{(diào)用的形式設(shè)置通用樣式。
@Component
struct?MyComponent2?{
??build()?{
????Button(`Hello?World`)
??}
}@Entry
@Component
struct?MyComponent?{
??build()?{
????Row()?{
??????MyComponent2()
????????.width(200)
????????.height(300)
????????.backgroundColor(Color.Red)
????}
??}
}
說(shuō)明:
ArkUI給自定義組件設(shè)置樣式時(shí),相當(dāng)于給MyComponent2套了一個(gè)不可見的容器組件,而這些樣式是設(shè)置在容器組件上的,而非直接設(shè)置給MyComponent2的Button組件。通過(guò)渲染結(jié)果我們可以很清楚的看到,背景顏色紅色并沒有直接生效在Button上,而是生效在Button所處的開發(fā)者不可見的容器組件上。
二、頁(yè)面和自定義組件生命周期
在開始之前,我們先明確自定義組件和頁(yè)面的關(guān)系:
- 自定義組件:@Component裝飾的UI單元,可以組合多個(gè)系統(tǒng)組件實(shí)現(xiàn)UI的復(fù)用。
- 頁(yè)面:即應(yīng)用的UI頁(yè)面??梢杂梢粋€(gè)或者多個(gè)自定義組件組成,@Entry裝飾的自定義組件為頁(yè)面的入口組件,即頁(yè)面的根節(jié)點(diǎn),一個(gè)頁(yè)面有且僅能有一個(gè)@Entry。只有被@Entry裝飾的組件才可以調(diào)用頁(yè)面的生命周期。
頁(yè)面生命周期,即被@Entry裝飾的組件生命周期,提供以下生命周期接口:
- onPageShow:頁(yè)面每次顯示時(shí)觸發(fā)。
- onPageHide:頁(yè)面每次隱藏時(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裝飾的組件(首頁(yè))生命周期。
根據(jù)上面的流程圖,我們從自定義組件的初始創(chuàng)建、重新渲染和刪除來(lái)詳細(xì)解釋。
自定義組件的創(chuàng)建和渲染流程
- 自定義組件的創(chuàng)建:自定義組件的實(shí)例由ArkUI框架創(chuàng)建。
- 初始化自定義組件的成員變量:通過(guò)本地默認(rèn)值或者構(gòu)造方法傳遞參數(shù)來(lái)初始化自定義組件的成員變量,初始化順序?yàn)槌蓡T變量的定義順序。
- 如果開發(fā)者定義了aboutToAppear,則執(zhí)行aboutToAppear方法。
- 在首次渲染的時(shí)候,執(zhí)行build方法渲染系統(tǒng)組件,如果有自定義子組件,則創(chuàng)建自定義組件的實(shí)例。在執(zhí)行build()函數(shù)的過(guò)程中,框架會(huì)觀察每個(gè)狀態(tài)變量的讀取狀態(tài),將保存兩個(gè)map:
- 狀態(tài)變量?->?UI組件(包括ForEach和if)。
- UI組件?->?此組件的更新函數(shù),即一個(gè)lambda方法,作為build()函數(shù)的子集,創(chuàng)建對(duì)應(yīng)的UI組件并執(zhí)行其屬性方法,示意如下。
build() {...this.observeComponentCreation(() => {
????Button.create();})this.observeComponentCreation(() => {
????Text.create();})...
}
當(dāng)應(yīng)用在后臺(tái)啟動(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組件對(duì)應(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)被銷毀,對(duì)前端節(jié)點(diǎn)解引用,當(dāng)前端節(jié)點(diǎn)已經(jīng)沒有引用時(shí),將被JS虛擬機(jī)垃圾回收。
- 自定義組件和它的變量將被刪除,如果其有同步的變量,比如@Link、@Prop、@StorageLink,將從同步源上取消注冊(cè)。
不建議在生命周期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)用頁(yè)面的生命周期onPageShow() {console.info('Index?onPageShow');}//?只有被@Entry裝飾的組件才可以調(diào)用頁(yè)面的生命周期onPageHide() {console.info('Index?onPageHide');}//?只有被@Entry裝飾的組件才可以調(diào)用頁(yè)面的生命周期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?aboutToAppearif (this.showChild) {Child()}//?this.showChild為false,刪除Child子組件,執(zhí)行Child?aboutToDisappearButton('delete?Child').onClick(() => {this.showChild?= false;})//?push到Page2頁(yè)面,執(zhí)行onPageHideButton('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頁(yè)面包含兩個(gè)自定義組件,一個(gè)是被@Entry裝飾的MyComponent,也是頁(yè)面的入口組件,即頁(yè)面的根節(jié)點(diǎn);一個(gè)是Child,是MyComponent的子組件。只有@Entry裝飾的節(jié)點(diǎn)才可以生效頁(yè)面的生命周期方法,所以MyComponent中聲明了當(dāng)前Index頁(yè)面的頁(yè)面生命周期函數(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è)頁(yè)面,當(dāng)前Index頁(yè)面隱藏,執(zhí)行頁(yè)面生命周期Index?onPageHide。此處調(diào)用的是router.pushUrl接口,Index頁(yè)面被隱藏,并沒有銷毀,所以只調(diào)用onPageHide。跳轉(zhuǎn)到新頁(yè)面后,執(zhí)行初始化新頁(yè)面的生命周期的流程。
- 如果調(diào)用的是router.replaceUrl,則當(dāng)前Index頁(yè)面被銷毀,執(zhí)行的生命周期流程將變?yōu)?#xff1a;Index?onPageHide?-->?MyComponent?aboutToDisappear?-->?Child?aboutToDisappear。上文已經(jīng)提到,組件的銷毀是從組件樹上直接摘下子樹,所以先調(diào)用父組件的aboutToDisappear,再調(diào)用子組件的aboutToDisappear,然后執(zhí)行初始化新頁(yè)面的生命周期流程。
- 點(diǎn)擊返回按鈕,觸發(fā)頁(yè)面生命周期Index?onBackPress,且觸發(fā)返回一個(gè)頁(yè)面后會(huì)導(dǎo)致當(dāng)前Index頁(yè)面被銷毀。
- 最小化應(yīng)用或者應(yīng)用進(jìn)入后臺(tái),觸發(fā)Index?onPageHide。當(dāng)前Index頁(yè)面沒有被銷毀,所以并不會(huì)執(zhí)行組件的aboutToDisappear。應(yīng)用回到前臺(tái),執(zhí)行Index?onPageShow。
- 退出應(yīng)用,執(zhí)行Index?onPageHide?-->?MyComponent?aboutToDisappear?-->?Child?aboutToDisappear。