[二、狀態管理]2管理元件擁有的狀態(4)@Provide裝飾器和@Consume裝飾器:與後代元件雙向同步

为敢技术發表於2024-07-10

@Provide和@Consume,應用於與後代元件的雙向資料同步,應用於狀態資料在多個層級之間傳遞的場景。不同於上文提到的父子元件之間透過命名引數機制傳遞,@Provide和@Consume擺脫引數傳遞機制的束縛,實現跨層級傳遞。

其中@Provide裝飾的變數是在祖先節點中,可以理解為被“提供”給後代的狀態變數。@Consume裝飾的變數是在後代元件中,去“消費(繫結)”祖先節點提供的變數。

說明

從API version 9開始,這兩個裝飾器支援在ArkTS卡片中使用。

概述

@Provide/@Consume裝飾的狀態變數有以下特性:

  • @Provide裝飾的狀態變數自動對其所有後代元件可用,即該變數被“provide”給他的後代元件。由此可見,@Provide的方便之處在於,開發者不需要多次在元件之間傳遞變數。
  • 後代透過使用@Consume去獲取@Provide提供的變數,建立在@Provide和@Consume之間的雙向資料同步,與@State/@Link不同的是,前者可以在多層級的父子元件之間傳遞。
  • @Provide和@Consume可以透過相同的變數名或者相同的變數別名繫結,變數型別必須相同。
// 透過相同的變數名繫結
@Provide a: number = 0;
@Consume a: number;

// 透過相同的變數別名繫結
@Provide('a') b: number = 0;
@Consume('a') c: number;

@Provide和@Consume透過相同的變數名或者相同的變數別名繫結時,@Provide修飾的變數和@Consume修飾的變數是一對多的關係。不允許在同一個自定義元件內,包括其子元件中宣告多個同名或者同別名的@Provide裝飾的變數。

裝飾器說明

@State的規則同樣適用於@Provide,差異為@Provide還作為多層後代的同步源。

@Provide變數裝飾器

說明

裝飾器引數

別名:常量字串,可選。

如果指定了別名,則透過別名來繫結變數;如果未指定別名,則透過變數名繫結變數。

同步型別

雙向同步。

從@Provide變數到所有@Consume變數以及相反的方向的資料同步。雙向同步的操作與@State和@Link的組合相同。

允許裝飾的變數型別

Object、class、string、number、boolean、enum型別,以及這些型別的陣列。巢狀型別的場景請參考觀察變化

不支援any,不支援簡單型別和複雜型別的聯合型別,不允許使用undefined和null。

必須指定型別。@Provide變數的@Consume變數的型別必須相同。

說明

不支援Length、ResourceStr、ResourceColor型別,Length、ResourceStr、ResourceColor為簡單型別和複雜型別的聯合型別。

被裝飾變數的初始值

必須指定。

@Consume變數裝飾器

說明

裝飾器引數

別名:常量字串,可選。

如果提供了別名,則必須有@Provide的變數和其有相同的別名才可以匹配成功;否則,則需要變數名相同才能匹配成功。

同步型別

雙向:從@Provide變數(具體請參見@Provide)到所有@Consume變數,以及相反的方向。雙向同步操作與@State和@Link的組合相同。

允許裝飾的變數型別

Object、class、string、number、boolean、enum型別,以及這些型別的陣列。巢狀型別的場景請參考觀察變化

不支援any,不允許使用undefined和null。

必須指定型別。@Provide變數的@Consume變數的型別必須相同。

說明
  • @Consume裝飾的變數,在其父節點或者祖先節點上,必須有對應的屬性和別名的@Provide裝飾的變數。

被裝飾變數的初始值

無,禁止本地初始化。

變數的傳遞/訪問規則說明

@Provide傳遞/訪問

說明

從父元件初始化和更新

可選,允許父元件中常規變數、@State、@Link、@Prop、@Provide、@Consume、@ObjectLink、@StorageLink、@StorageProp、@LocalStorageLink和@LocalStorageProp裝飾的變數裝飾變數初始化子元件@Provide。

用於初始化子元件

允許,可用於初始化@State、@Link、@Prop、@Provide。

和父元件同步

否。

和後代元件同步

和@Consume雙向同步。

是否支援元件外訪問

私有,僅可以在所屬元件內訪問。

圖1 @Provide初始化規則圖示

@Consume傳遞/訪問

說明

從父元件初始化和更新

禁止。透過相同的變數名和alias(別名)從@Provide初始化。

用於初始化子元件

允許,可用於初始化@State、@Link、@Prop、@Provide。

和祖先元件同步

和@Provide雙向同步。

是否支援元件外訪問

私有,僅可以在所屬元件內訪問

圖2 @Consume初始化規則圖示

觀察變化和行為表現

觀察變化

  • 當裝飾的資料型別為boolean、string、number型別時,可以觀察到數值的變化。
  • 當裝飾的資料型別為class或者Object的時候,可以觀察到賦值和屬性賦值的變化(屬性為Object.keys(observedObject)返回的所有屬性)。
  • 當裝飾的物件是array的時候,可以觀察到陣列的新增、刪除、更新陣列單元。

框架行為

  1. 初始渲染:
    1. @Provide裝飾的變數會以map的形式,傳遞給當前@Provide所屬元件的所有子元件;
    2. 子元件中如果使用@Consume變數,則會在map中查詢是否有該變數名/alias(別名)對應的@Provide的變數,如果查詢不到,框架會丟擲JS ERROR;
    3. 在初始化@Consume變數時,和@State/@Link的流程類似,@Consume變數會儲存在map中查詢到的@Provide變數,並把自己註冊給@Provide。
  2. 當@Provide裝飾的資料變化時:
    1. 透過初始渲染的步驟可知,子元件@Consume已把自己註冊給父元件。父元件@Provide變數變更後,會遍歷更新所有依賴它的系統元件(elementid)和狀態變數(@Consume);
    2. 通知@Consume更新後,子元件所有依賴@Consume的系統元件(elementId)都會被通知更新。以此實現@Provide對@Consume狀態資料同步。
  3. 當@Consume裝飾的資料變化時:
    1. 透過初始渲染的步驟可知,子元件@Consume持有@Provide的例項。在@Consume更新後呼叫@Provide的更新方法,將更新的數值同步回@Provide,以此實現@Consume向@Provide的同步更新。

使用場景

在下面的示例是與後代元件雙向同步狀態@Provide和@Consume場景。當分別點選CompA和CompD元件內Button時,reviewVotes 的更改會雙向同步在CompA和CompD中。

@Component
struct CompD {
  // @Consume裝飾的變數透過相同的屬性名繫結其祖先元件CompA內的@Provide裝飾的變數
  @Consume reviewVotes: number;

  build() {
    Column() {
      Text(`reviewVotes(${this.reviewVotes})`)
      Button(`reviewVotes(${this.reviewVotes}), give +1`)
        .onClick(() => this.reviewVotes += 1)
    }
    .width('50%')
  }
}

@Component
struct CompC {
  build() {
    Row({ space: 5 }) {
      CompD()
      CompD()
    }
  }
}

@Component
struct CompB {
  build() {
    CompC()
  }
}

@Entry
@Component
struct CompA {
  // @Provide裝飾的變數reviewVotes由入口元件CompA提供其後代元件
  @Provide reviewVotes: number = 0;

  build() {
    Column() {
      Button(`reviewVotes(${this.reviewVotes}), give +1`)
        .onClick(() => this.reviewVotes += 1)
      CompB()
    }
  }
}

相關文章