渲染控制 - if/else、ForEach

if/else:條件渲染

ArkTS提供了渲染控制的能力。條件渲染可根據(jù)應(yīng)用的不同狀態(tài),使用if、else和else if渲染對應(yīng)狀態(tài)下的UI內(nèi)容。

使用規(guī)則

  • 支持if、else和else if語句。
  • if、else if后跟隨的條件語句可以使用狀態(tài)變量。
  • 允許在容器組件內(nèi)使用,通過條件渲染語句構(gòu)建不同的子組件。
  • 條件渲染語句在涉及到組件的父子關(guān)系時(shí)是“透明”的,當(dāng)父組件和子組件之間存在一個(gè)或多個(gè)if語句時(shí),必須遵守父組件關(guān)于子組件使用的規(guī)則。
  • 每個(gè)分支內(nèi)部的構(gòu)建函數(shù)必須遵循構(gòu)建函數(shù)的規(guī)則,并創(chuàng)建一個(gè)或多個(gè)組件。無法創(chuàng)建組件的空構(gòu)建函數(shù)會(huì)產(chǎn)生語法錯(cuò)誤。
  • 某些容器組件限制子組件的類型或數(shù)量,將條件渲染語句用于這些組件內(nèi)時(shí),這些限制將同樣應(yīng)用于條件渲染語句內(nèi)創(chuàng)建的組件。例如,Grid容器組件的子組件僅支持GridItem組件,在Grid內(nèi)使用條件渲染語句時(shí),條件渲染語句內(nèi)僅允許使用GridItem組件。

更新機(jī)制

當(dāng)if、else if后跟隨的狀態(tài)判斷中使用的狀態(tài)變量值變化時(shí),條件渲染語句會(huì)進(jìn)行更新,更新步驟如下:

  1. 評估if和else if的狀態(tài)判斷條件,如果分支沒有變化,無需執(zhí)行以下步驟。如果分支有變化,則執(zhí)行2、3步驟:
  2. 刪除此前構(gòu)建的所有子組件。
  3. 執(zhí)行新分支的構(gòu)造函數(shù),將獲取到的組件添加到if父容器中。如果缺少適用的else分支,則不構(gòu)建任何內(nèi)容。

條件可以包括Typescript表達(dá)式。對于構(gòu)造函數(shù)中的表達(dá)式,此類表達(dá)式不得更改應(yīng)用程序狀態(tài)。

ForEach:循環(huán)渲染

ForEach接口基于數(shù)組類型數(shù)據(jù)來進(jìn)行循環(huán)渲染,需要與容器組件配合使用,且接口返回的組件應(yīng)當(dāng)是允許包含在ForEach父容器組件中的子組件。例如,ListItem組件要求ForEach的父容器組件必須為List組件。

接口描述

ForEach(
  arr: Array,
  itemGenerator: (item: any, index?: number) => void,
  keyGenerator?: (item: any, index?: number) => string
)
image.png

鍵值生成規(guī)則

在ForEach循環(huán)渲染過程中,系統(tǒng)會(huì)為每個(gè)數(shù)組元素生成一個(gè)唯一且持久的鍵值,用于標(biāo)識(shí)對應(yīng)的組件。當(dāng)這個(gè)鍵值變化時(shí),ArkUI框架將視為該數(shù)組元素已被替換或修改,并會(huì)基于新的鍵值創(chuàng)建一個(gè)新的組件。

ForEach提供了一個(gè)名為keyGenerator的參數(shù),這是一個(gè)函數(shù),開發(fā)者可以通過它自定義鍵值的生成規(guī)則。如果開發(fā)者沒有定義keyGenerator函數(shù),則ArkUI框架會(huì)使用默認(rèn)的鍵值生成函數(shù),即(item: any, index: number) => { return index + '__' + JSON.stringify(item); }。

ArkUI框架對于ForEach的鍵值生成有一套特定的判斷規(guī)則,這主要與itemGenerator函數(shù)的第二個(gè)參數(shù)index以及keyGenerator函數(shù)的第二個(gè)參數(shù)index有關(guān),具體的鍵值生成規(guī)則判斷邏輯如下圖所示。

ForEach鍵值生成規(guī)則

image.png

注意:ArkUI框架會(huì)對重復(fù)的鍵值發(fā)出警告。在UI更新的場景下,如果出現(xiàn)重復(fù)的鍵值,框架可能無法正常工作,具體請參見渲染結(jié)果非預(yù)期。

組件創(chuàng)建規(guī)則

在確定鍵值生成規(guī)則后,F(xiàn)orEach的第二個(gè)參數(shù)itemGenerator函數(shù)會(huì)根據(jù)鍵值生成規(guī)則為數(shù)據(jù)源的每個(gè)數(shù)組項(xiàng)創(chuàng)建組件。組件的創(chuàng)建包括兩種情況:ForEach首次渲染ForEach非首次渲染。

首次渲染

在ForEach首次渲染時(shí),會(huì)根據(jù)前述鍵值生成規(guī)則為數(shù)據(jù)源的每個(gè)數(shù)組項(xiàng)生成唯一鍵值,并創(chuàng)建相應(yīng)的組件。

@Entry
@Component
struct Parent {
  @State simpleList: Array<string> = ['one', 'two', 'three'];

  build() {
    Row() {
      Column() {
        ForEach(this.simpleList, (item: string) => {
          ChildItem({ item: item })
        }, (item: string) => item)
      }
      .width('100%')
      .height('100%')
    }
    .height('100%')
    .backgroundColor(0xF1F3F5)
  }
}

@Component
struct ChildItem {
  @Prop item: string;

  build() {
    Text(this.item)
      .fontSize(50)
  }
}

非首次渲染

在ForEach組件進(jìn)行非首次渲染時(shí),它會(huì)檢查新生成的鍵值是否在上次渲染中已經(jīng)存在。如果鍵值不存在,則會(huì)創(chuàng)建一個(gè)新的組件;如果鍵值存在,則不會(huì)創(chuàng)建新的組件,而是直接渲染該鍵值所對應(yīng)的組件。例如,在以下的代碼示例中,通過點(diǎn)擊事件修改了數(shù)組的第三項(xiàng)值為"new three",這將觸發(fā)ForEach組件進(jìn)行非首次渲染。

@Entry
@Component
struct Parent {
  @State simpleList: Array<string> = ['one', 'two', 'three'];

  build() {
    Row() {
      Column() {
        Text('點(diǎn)擊修改第3個(gè)數(shù)組項(xiàng)的值')
          .fontSize(24)
          .fontColor(Color.Red)
          .onClick(() => {
            this.simpleList[2] = 'new three';
          })

        ForEach(this.simpleList, (item: string) => {
          ChildItem({ item: item })
            .margin({ top: 20 })
        }, (item: string) => item)
      }
      .justifyContent(FlexAlign.Center)
      .width('100%')
      .height('100%')
    }
    .height('100%')
    .backgroundColor(0xF1F3F5)
  }
}

@Component
struct ChildItem {
  @Prop item: string;

  build() {
    Text(this.item)
      .fontSize(30)
  }
}
0000000000011111111.20240110113019.17460204640259301359757476130390.gif

從本例可以看出@State 能夠監(jiān)聽到簡單數(shù)據(jù)類型數(shù)組數(shù)據(jù)源 simpleList 數(shù)組項(xiàng)的變化。

  1. 當(dāng) simpleList 數(shù)組項(xiàng)發(fā)生變化時(shí),會(huì)觸發(fā) ForEach 進(jìn)行重新渲染。
  2. ForEach 遍歷新的數(shù)據(jù)源 ['one', 'two', 'new three'],并生成對應(yīng)的鍵值one、two和new three。
  3. 其中,鍵值one和two在上次渲染中已經(jīng)存在,所以 ForEach 復(fù)用了對應(yīng)的組件并進(jìn)行了渲染。對于第三個(gè)數(shù)組項(xiàng) "new three",由于其通過鍵值生成規(guī)則 item 生成的鍵值new three在上次渲染中不存在,因此 ForEach 為該數(shù)組項(xiàng)創(chuàng)建了一個(gè)新的組件。

使用建議

  • 盡量避免在最終的鍵值生成規(guī)則中包含數(shù)據(jù)項(xiàng)索引index,以防止出現(xiàn)渲染結(jié)果非預(yù)期渲染性能降低。如果業(yè)務(wù)確實(shí)需要使用index,例如列表需要通過index進(jìn)行條件渲染,開發(fā)者需要接受ForEach在改變數(shù)據(jù)源后重新創(chuàng)建組件所帶來的性能損耗。
  • 為滿足鍵值的唯一性,對于對象數(shù)據(jù)類型,建議使用對象數(shù)據(jù)中的唯一id作為鍵值。
  • 基本數(shù)據(jù)類型的數(shù)據(jù)項(xiàng)沒有唯一ID屬性。如果使用基本數(shù)據(jù)類型本身作為鍵值,必須確保數(shù)組項(xiàng)無重復(fù)。因此,對于數(shù)據(jù)源會(huì)發(fā)生變化的場景,建議將基本數(shù)據(jù)類型數(shù)組轉(zhuǎn)化為具備唯一ID屬性的對象數(shù)據(jù)類型數(shù)組,再使用ID屬性作為鍵值生成規(guī)則。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容