HarmonyOS NEXT應(yīng)用開發(fā)之@Prop裝飾器:父子單向同步

@Prop裝飾的變量可以和父組件建立單向的同步關(guān)系。@Prop裝飾的變量是可變的毙驯,但是變化不會(huì)同步回其父組件回右。

說明:
從API version 9開始供璧,該裝飾器支持在ArkTS卡片中使用。

概述

@Prop裝飾的變量和父組件建立單向的同步關(guān)系:

  • @Prop變量允許在本地修改熏迹,但修改后的變化不會(huì)同步回父組件。

  • 當(dāng)數(shù)據(jù)源更改時(shí),@Prop裝飾的變量都會(huì)更新受啥,并且會(huì)覆蓋本地所有更改。因此鸽心,數(shù)值的同步是父組件到子組件(所屬組件)滚局,子組件數(shù)值的變化不會(huì)同步到父組件。

限制條件

  • @Prop裝飾變量時(shí)會(huì)進(jìn)行深拷貝顽频,在拷貝的過程中除了基本類型藤肢、Map、Set糯景、Date嘁圈、Array外,都會(huì)丟失類型蟀淮。例如PixelMap 等通過NAPI提供的復(fù)雜類型最住,由于有部分實(shí)現(xiàn)在Native側(cè),因此無法在ArkTS側(cè)通過深拷貝獲得完整的數(shù)據(jù)怠惶。

  • @Prop裝飾器不能在@Entry裝飾的自定義組件中使用涨缚。

裝飾器使用規(guī)則說明

@Prop變量裝飾器 說明
裝飾器參數(shù)
同步類型 單向同步:對(duì)父組件狀態(tài)變量值的修改,將同步給子組件@Prop裝飾的變量策治,子組件@Prop變量的修改不會(huì)同步到父組件的狀態(tài)變量上脓魏。嵌套類型的場景請(qǐng)參考 觀察變化 。
允許裝飾的變量類型 Object通惫、class茂翔、string、number履腋、boolean珊燎、enum類型,以及這些類型的數(shù)組府树。
不支持any俐末,支持undefined和null。
支持Date類型奄侠。
API11及以上支持Map卓箫、Set類型。
支持類型的場景請(qǐng)參考 觀察變化 垄潮。
API11及以上支持上述支持類型的聯(lián)合類型烹卒,比如string | number, string | undefined 或者 ClassA | null闷盔,示例見 Prop支持聯(lián)合類型實(shí)例 。
注意
當(dāng)使用undefined和null的時(shí)候旅急,建議顯式指定類型逢勾,遵循TypeScipt類型校驗(yàn),比如:@Prop a : string | undefined = undefined是推薦的藐吮,不推薦@Prop a: string = undefined溺拱。
支持AkrUI框架定義的聯(lián)合類型Length、ResourceStr谣辞、ResourceColor類型迫摔。 必須指定類型。
@Prop和 數(shù)據(jù)源 類型需要相同泥从,有以下三種情況:
-?@Prop裝飾的變量和@State以及其他裝飾器同步時(shí)雙方的類型必須相同句占,示例請(qǐng)參考 父組件@State到子組件@Prop簡單數(shù)據(jù)類型同步 。
-?@Prop裝飾的變量和@State以及其他裝飾器裝飾的數(shù)組的項(xiàng)同步時(shí) 躯嫉,@Prop的類型需要和@State裝飾的數(shù)組的數(shù)組項(xiàng)相同纱烘,比如@Prop?:?T和@State?:?Array<T>,示例請(qǐng)參考 父組件@State數(shù)組中的項(xiàng)到子組件@Prop簡單數(shù)據(jù)類型同步 祈餐。
-?當(dāng)父組件狀態(tài)變量為Object或者class時(shí)擂啥,@Prop裝飾的變量和父組件狀態(tài)變量的屬性類型相同,示例請(qǐng)參考 從父組件中的@State類對(duì)象屬性到@Prop簡單類型的同步 昼弟。
嵌套傳遞層數(shù) 在組件復(fù)用場景啤它,建議@Prop深度嵌套數(shù)據(jù)不要超過5層,嵌套太多會(huì)導(dǎo)致深拷貝占用的空間過大以及GarbageCollection(垃圾回收)舱痘,引起性能問題变骡,此時(shí)更建議使用 @ObjectLink 。
被裝飾變量的初始值 允許本地初始化芭逝。如果在API 11中和 @Require 結(jié)合使用塌碌,則必須父組件構(gòu)造傳參。

變量的傳遞/訪問規(guī)則說明

傳遞/訪問 說明
從父組件初始化 如果本地有初始化旬盯,則是可選的台妆。沒有的話,則必選胖翰,支持父組件中的常規(guī)變量(常規(guī)變量對(duì)@Prop賦值接剩,只是數(shù)值的初始化,常規(guī)變量的變化不會(huì)觸發(fā)UI刷新萨咳。只有狀態(tài)變量才能觸發(fā)UI刷新)懊缺、@State、@Link培他、@Prop鹃两、@Provide遗座、@Consume、@ObjectLink俊扳、@StorageLink途蒋、@StorageProp、@LocalStorageLink和@LocalStorageProp去初始化子組件中的@Prop變量馋记。
用于初始化子組件 @Prop支持去初始化子組件中的常規(guī)變量号坡、@State、@Link抗果、@Prop筋帖、@Provide奸晴。
是否支持組件外訪問 @Prop裝飾的變量是私有的冤馏,只能在組件內(nèi)訪問。

圖1 初始化規(guī)則圖示

觀察變化和行為表現(xiàn)

觀察變化

@Prop裝飾的數(shù)據(jù)可以觀察到以下變化寄啼。

  • 當(dāng)裝飾的類型是允許的類型逮光,即Object、class墩划、string涕刚、number、boolean乙帮、enum類型都可以觀察到賦值的變化杜漠。

    // 簡單類型
    @Prop count: number;
    // 賦值的變化可以被觀察到
    this.count = 1;
    // 復(fù)雜類型
    @Prop title: Model;
    // 可以觀察到賦值的變化
    this.title = new Model('Hi');
    
  • 當(dāng)裝飾的類型是Object或者class復(fù)雜類型時(shí),可以觀察到第一層的屬性的變化察净,屬性即Object.keys(observedObject)返回的所有屬性驾茴;

class ClassA {
  public value: string;
  constructor(value: string) {
    this.value = value;
  }
}
class Model {
  public value: string;
  public a: ClassA;
  constructor(value: string, a: ClassA) {
    this.value = value;
    this.a = a;
  }
}

@Prop title: Model;
// 可以觀察到第一層的變化
this.title.value = 'Hi'
// 觀察不到第二層的變化
this.title.a.value = 'ArkUi' 

對(duì)于嵌套場景,如果class是被@Observed裝飾的氢卡,可以觀察到class屬性的變化锈至,示例請(qǐng)參考@Prop嵌套場景

  • 當(dāng)裝飾的類型是數(shù)組的時(shí)候译秦,可以觀察到數(shù)組本身的賦值和數(shù)組項(xiàng)的添加峡捡、刪除和更新。
// @State裝飾的對(duì)象為數(shù)組時(shí)
@Prop title: string[]
// 數(shù)組自身的賦值可以觀察到
this.title = ['1']
// 數(shù)組項(xiàng)的賦值可以觀察到
this.title[0] = '2'
// 刪除數(shù)組項(xiàng)可以觀察到
this.title.pop()
// 新增數(shù)組項(xiàng)可以觀察到
this.title.push('3')

對(duì)于@State和@Prop的同步場景:

  • 使用父組件中@State變量的值初始化子組件中的@Prop變量筑悴。當(dāng)@State變量變化時(shí)们拙,該變量值也會(huì)同步更新至@Prop變量。

  • @Prop裝飾的變量的修改不會(huì)影響其數(shù)據(jù)源@State裝飾變量的值阁吝。

  • 除了@State砚婆,數(shù)據(jù)源也可以用@Link或@Prop裝飾,對(duì)@Prop的同步機(jī)制是相同的求摇。

  • 數(shù)據(jù)源和@Prop變量的類型需要相同射沟,@Prop允許簡單類型和class類型殊者。

  • 當(dāng)裝飾的對(duì)象是Date時(shí),可以觀察到Date整體的賦值验夯,同時(shí)可通過調(diào)用Date的接口setFullYear, setMonth, setDate, setHours, setMinutes, setSeconds, setMilliseconds, setTime, setUTCFullYear, setUTCMonth, setUTCDate, setUTCHours, setUTCMinutes, setUTCSeconds, setUTCMilliseconds 更新Date的屬性猖吴。

@Component
struct DateComponent {
  @Prop selectedDate: Date = new Date('');

  build() {
    Column() {
      Button('child update the new date')
        .margin(10)
        .onClick(() => {
          this.selectedDate = new Date('2023-09-09')
        })
      Button(`child increase the year by 1`).onClick(() => {
        this.selectedDate.setFullYear(this.selectedDate.getFullYear() + 1)
      })
      DatePicker({
        start: new Date('1970-1-1'),
        end: new Date('2100-1-1'),
        selected: this.selectedDate
      })
    }
  }
}

@Entry
@Component
struct ParentComponent {
  @State parentSelectedDate: Date = new Date('2021-08-08');

  build() {
    Column() {
      Button('parent update the new date')
        .margin(10)
        .onClick(() => {
          this.parentSelectedDate = new Date('2023-07-07')
        })
      Button('parent increase the day by 1')
        .margin(10)
        .onClick(() => {
          this.parentSelectedDate.setDate(this.parentSelectedDate.getDate() + 1)
        })
      DatePicker({
        start: new Date('1970-1-1'),
        end: new Date('2100-1-1'),
        selected: this.parentSelectedDate
      })

      DateComponent({ selectedDate: this.parentSelectedDate })
    }

  }
}
  • 當(dāng)裝飾的變量是Map時(shí),可以觀察到Map整體的賦值挥转,同時(shí)可通過調(diào)用Map的接口set, clear, delete 更新Map的值海蔽。

  • 當(dāng)裝飾的變量是Set時(shí),可以觀察到Set整體的賦值绑谣,同時(shí)可通過調(diào)用Set的接口add, clear, delete 更新Set的值党窜。

框架行為

要理解@Prop變量值初始化和更新機(jī)制,有必要了解父組件和擁有@Prop變量的子組件初始渲染和更新流程借宵。

  1. 初始渲染:

    1. 執(zhí)行父組件的build()函數(shù)將創(chuàng)建子組件的新實(shí)例幌衣,將數(shù)據(jù)源傳遞給子組件;
    2. 初始化子組件@Prop裝飾的變量壤玫。
  2. 更新:

    1. 子組件@Prop更新時(shí)豁护,更新僅停留在當(dāng)前子組件,不會(huì)同步回父組件欲间;
    2. 當(dāng)父組件的數(shù)據(jù)源更新時(shí)楚里,子組件的@Prop裝飾的變量將被來自父組件的數(shù)據(jù)源重置,所有@Prop裝飾的本地的修改將被父組件的更新覆蓋猎贴。

說明:
@Prop裝飾的數(shù)據(jù)更新依賴其所屬自定義組件的重新渲染班缎,所以在應(yīng)用進(jìn)入后臺(tái)后,@Prop無法刷新她渴,推薦使用@Link代替达址。

使用場景

父組件@State到子組件@Prop簡單數(shù)據(jù)類型同步

以下示例是@State到子組件@Prop簡單數(shù)據(jù)同步,父組件ParentComponent的狀態(tài)變量countDownStartValue初始化子組件CountDownComponent中@Prop裝飾的count惹骂,點(diǎn)擊“Try again”苏携,count的修改僅保留在CountDownComponent 不會(huì)同步給父組件ParentComponent。

ParentComponent的狀態(tài)變量countDownStartValue的變化將重置CountDownComponent的count对粪。

@Component
struct CountDownComponent {
  @Prop count: number = 0;
  costOfOneAttempt: number = 1;

  build() {
    Column() {
      if (this.count > 0) {
        Text(`You have ${this.count} Nuggets left`)
      } else {
        Text('Game over!')
      }
      // @Prop裝飾的變量不會(huì)同步給父組件
      Button(`Try again`).onClick(() => {
        this.count -= this.costOfOneAttempt;
      })
    }
  }
}

@Entry
@Component
struct ParentComponent {
  @State countDownStartValue: number = 10;

  build() {
    Column() {
      Text(`Grant ${this.countDownStartValue} nuggets to play.`)
      // 父組件的數(shù)據(jù)源的修改會(huì)同步給子組件
      Button(`+1 - Nuggets in New Game`).onClick(() => {
        this.countDownStartValue += 1;
      })
      // 父組件的修改會(huì)同步給子組件
      Button(`-1  - Nuggets in New Game`).onClick(() => {
        this.countDownStartValue -= 1;
      })

      CountDownComponent({ count: this.countDownStartValue, costOfOneAttempt: 2 })
    }
  }
}

在上面的示例中:

  1. CountDownComponent子組件首次創(chuàng)建時(shí)其@Prop裝飾的count變量將從父組件@State裝飾的countDownStartValue變量初始化右冻;

  2. 按“+1”或“-1”按鈕時(shí),父組件的@State裝飾的countDownStartValue值會(huì)變化著拭,這將觸發(fā)父組件重新渲染纱扭,在父組件重新渲染過程中會(huì)刷新使用countDownStartValue狀態(tài)變量的UI組件并單向同步更新CountDownComponent子組件中的count值;

  3. 更新count狀態(tài)變量值也會(huì)觸發(fā)CountDownComponent的重新渲染儡遮,在重新渲染過程中乳蛾,評(píng)估使用count狀態(tài)變量的if語句條件(this.count > 0),并執(zhí)行true分支中的使用count狀態(tài)變量的UI組件相關(guān)描述來更新Text組件的UI顯示;

  4. 當(dāng)按下子組件CountDownComponent的“Try again”按鈕時(shí)肃叶,其@Prop變量count將被更改蹂随,但是count值的更改不會(huì)影響父組件的countDownStartValue值;

  5. 父組件的countDownStartValue值會(huì)變化時(shí)因惭,父組件的修改將覆蓋掉子組件CountDownComponent中count本地的修改岳锁。

父組件@State數(shù)組項(xiàng)到子組件@Prop簡單數(shù)據(jù)類型同步

父組件中@State如果裝飾的數(shù)組,其數(shù)組項(xiàng)也可以初始化@Prop蹦魔。以下示例中父組件Index中@State裝飾的數(shù)組arr激率,將其數(shù)組項(xiàng)初始化子組件Child中@Prop裝飾的value。

@Component
struct Child {
  @Prop value: number = 0;

  build() {
    Text(`${this.value}`)
      .fontSize(50)
      .onClick(() => {
        this.value++
      })
  }
}

@Entry
@Component
struct Index {
  @State arr: number[] = [1, 2, 3];

  build() {
    Row() {
      Column() {
        Child({ value: this.arr[0] })
        Child({ value: this.arr[1] })
        Child({ value: this.arr[2] })

        Divider().height(5)

        ForEach(this.arr,
          (item: number) => {
            Child({ value: item })
          },
          (item: string) => item.toString()
        )
        Text('replace entire arr')
          .fontSize(50)
          .onClick(() => {
            // 兩個(gè)數(shù)組都包含項(xiàng)“3”勿决。
            this.arr = this.arr[0] == 1 ? [3, 4, 5] : [1, 2, 3];
          })
      }
    }
  }
}

初始渲染創(chuàng)建6個(gè)子組件實(shí)例乒躺,每個(gè)@Prop裝飾的變量初始化都在本地拷貝了一份數(shù)組項(xiàng)。子組件onclick事件處理程序會(huì)更改局部變量值低缩。

如果點(diǎn)擊界面上的“1”六下嘉冒,“2”五下、“3”四下表制,將所有變量的本地取值都變?yōu)椤?”健爬。

7
7
7
----
7
7
7

單擊replace entire arr后,屏幕將顯示以下信息么介。

3
4
5
----
7
4
5
  • 在子組件Child中做的所有的修改都不會(huì)同步回父組件Index組件,所以即使6個(gè)組件顯示都為7蜕衡,但在父組件Index中壤短,this.arr保存的值依舊是[1,2,3]。

  • 點(diǎn)擊replace entire arr慨仿,this.arr[0] == 1成立久脯,將this.arr賦值為[3, 4, 5];

  • 因?yàn)閠his.arr[0]已更改镰吆,Child({value: this.arr[0]})組件將this.arr[0]更新同步到實(shí)例@Prop裝飾的變量帘撰。Child({value: this.arr[1]})和Child({value: this.arr[2]})的情況也類似。

  • this.arr的更改觸發(fā)ForEach更新万皿,this.arr更新的前后都有數(shù)值為3的數(shù)組項(xiàng):[3, 4, 5] 和[1, 2, 3]摧找。根據(jù)diff算法,數(shù)組項(xiàng)“3”將被保留牢硅,刪除“1”和“2”的數(shù)組項(xiàng)蹬耘,添加為“4”和“5”的數(shù)組項(xiàng)。這就意味著减余,數(shù)組項(xiàng)“3”的組件不會(huì)重新生成综苔,而是將其移動(dòng)到第一位。所以“3”對(duì)應(yīng)的組件不會(huì)更新,此時(shí)“3”對(duì)應(yīng)的組件數(shù)值為“7”如筛,F(xiàn)orEach最終的渲染結(jié)果是“7”堡牡,“4”,“5”杨刨。

從父組件中的@State類對(duì)象屬性到@Prop簡單類型的同步

如果圖書館有一本圖書和兩位用戶悴侵,每位用戶都可以將圖書標(biāo)記為已讀,此標(biāo)記行為不會(huì)影響其它讀者用戶拭嫁。從代碼角度講可免,對(duì)@Prop圖書對(duì)象的本地更改不會(huì)同步給圖書館組件中的@State圖書對(duì)象。

在此示例中做粤,圖書類可以使用@Observed裝飾器浇借,但不是必須的,只有在嵌套結(jié)構(gòu)時(shí)需要此裝飾器怕品。這一點(diǎn)我們會(huì)在 從父組件中的@State數(shù)組項(xiàng)到@Prop class類型的同步 說明妇垢。

class Book {
  public title: string;
  public pages: number;
  public readIt: boolean = false;

  constructor(title: string, pages: number) {
    this.title = title;
    this.pages = pages;
  }
}

@Component
struct ReaderComp {
  @Prop book: Book = new Book("", 0);

  build() {
    Row() {
      Text(this.book.title)
      Text(`...has${this.book.pages} pages!`)
      Text(`...${this.book.readIt ? "I have read" : 'I have not read it'}`)
        .onClick(() => this.book.readIt = true)
    }
  }
}

@Entry
@Component
struct Library {
  @State book: Book = new Book('100 secrets of C++', 765);

  build() {
    Column() {
      ReaderComp({ book: this.book })
      ReaderComp({ book: this.book })
    }
  }
}

從父組件中的@State數(shù)組項(xiàng)到@Prop class類型的同步

在下面的示例中,更改了@State 裝飾的allBooks數(shù)組中Book對(duì)象上的屬性肉康,但點(diǎn)擊“Mark read for everyone”無反應(yīng)闯估。這是因?yàn)樵搶傩允堑诙拥那短讓傩裕珸State裝飾器只能觀察到第一層屬性吼和,不會(huì)觀察到此屬性更改涨薪,所以框架不會(huì)更新ReaderComp。

let nextId: number = 1;

// @Observed
class Book {
  public id: number;
  public title: string;
  public pages: number;
  public readIt: boolean = false;

  constructor(title: string, pages: number) {
    this.id = nextId++;
    this.title = title;
    this.pages = pages;
  }
}

@Component
struct ReaderComp {
  @Prop book: Book = new Book("", 1);

  build() {
    Row() {
      Text(` ${this.book ? this.book.title : "Book is undefined"}`).fontColor('#e6000000')
      Text(` has ${this.book ? this.book.pages : "Book is undefined"} pages!`).fontColor('#e6000000')
      Text(` ${this.book ? this.book.readIt ? "I have read" : 'I have not read it' : "Book is undefined"}`).fontColor('#e6000000')
        .onClick(() => this.book.readIt = true)
    }
  }
}

@Entry
@Component
struct Library {
  @State allBooks: Book[] = [new Book("C#", 765), new Book("JS", 652), new Book("TS", 765)];

  build() {
    Column() {
      Text('library`s all time favorite')
        .width(312)
        .height(40)
        .backgroundColor('#0d000000')
        .borderRadius(20)
        .margin(12)
        .padding({ left: 20 })
        .fontColor('#e6000000')
      ReaderComp({ book: this.allBooks[2] })
        .backgroundColor('#0d000000')
        .width(312)
        .height(40)
        .padding({ left: 20, top: 10 })
        .borderRadius(20)
        .colorBlend('#e6000000')
      Divider()
      Text('Books on loaan to a reader')
        .width(312)
        .height(40)
        .backgroundColor('#0d000000')
        .borderRadius(20)
        .margin(12)
        .padding({ left: 20 })
        .fontColor('#e6000000')
      ForEach(this.allBooks, (book: Book) => {
        ReaderComp({ book: book })
          .margin(12)
          .width(312)
          .height(40)
          .padding({ left: 20, top: 10 })
          .backgroundColor('#0d000000')
          .borderRadius(20)
      },
        (book: Book) => book.id.toString())
      Button('Add new')
        .width(312)
        .height(40)
        .margin(12)
        .fontColor('#FFFFFF 90%')
        .onClick(() => {
          this.allBooks.push(new Book("JA", 512));
        })
      Button('Remove first book')
        .width(312)
        .height(40)
        .margin(12)
        .fontColor('#FFFFFF 90%')
        .onClick(() => {
          if (this.allBooks.length > 0){
            this.allBooks.shift();
          } else {
            console.log("length <= 0")
          }
        })
      Button("Mark read for everyone")
        .width(312)
        .height(40)
        .margin(12)
        .fontColor('#FFFFFF 90%')
        .onClick(() => {
          this.allBooks.forEach((book) => book.readIt = true)
        })
    }
  }
}

需要使用@Observed裝飾class Book炫乓,Book的屬性將被觀察刚夺。 需要注意的是,@Prop在子組件裝飾的狀態(tài)變量和父組件的數(shù)據(jù)源是單向同步關(guān)系末捣,即ReaderComp中的@Prop book的修改不會(huì)同步給父組件Library侠姑。而父組件只會(huì)在數(shù)值有更新的時(shí)候(和上一次狀態(tài)的對(duì)比),才會(huì)觸發(fā)UI的重新渲染箩做。

@Observed
class Book {
  public id: number;
  public title: string;
  public pages: number;
  public readIt: boolean = false;

  constructor(title: string, pages: number) {
    this.id = nextId++;
    this.title = title;
    this.pages = pages;
  }
}

@Observed裝飾的類的實(shí)例會(huì)被不透明的代理對(duì)象包裝莽红,此代理可以檢測到包裝對(duì)象內(nèi)的所有屬性更改。如果發(fā)生這種情況邦邦,此時(shí)安吁,代理通知@Prop,@Prop對(duì)象值被更新圃酵。

@Prop本地初始化不和父組件同步

為了支持@Component裝飾的組件復(fù)用場景柳畔,@Prop支持本地初始化,這樣可以讓@Prop是否與父組件建立同步關(guān)系變得可選郭赐。當(dāng)且僅當(dāng)@Prop有本地初始化時(shí)薪韩,從父組件向子組件傳遞@Prop的數(shù)據(jù)源才是可選的确沸。

下面的示例中,子組件包含兩個(gè)@Prop變量:

  • @Prop customCounter沒有本地初始化俘陷,所以需要父組件提供數(shù)據(jù)源去初始化@Prop罗捎,并當(dāng)父組件的數(shù)據(jù)源變化時(shí),@Prop也將被更新拉盾;

  • @Prop customCounter2有本地初始化桨菜,在這種情況下,@Prop依舊允許但非強(qiáng)制父組件同步數(shù)據(jù)源給@Prop捉偏。

@Component
struct MyComponent {
  @Prop customCounter: number;
  @Prop customCounter2: number = 5;

  build() {
    Column() {
      Row() {
        Text(`From Main: ${this.customCounter}`).fontColor('#ff6b6565').margin({ left: -110, top: 12 })
      }

      Row() {
        Button('Click to change locally !')
          .width(288)
          .height(40)
          .margin({ left: 30, top: 12 })
          .fontColor('#FFFFFF倒得,90%')
          .onClick(() => {
            this.customCounter2++
          })
      }

      Row() {
        Text(`Custom Local: ${this.customCounter2}`).fontColor('#ff6b6565').margin({ left: -110, top: 12 })
      }
    }
  }
}

@Entry
@Component
struct MainProgram {
  @State mainCounter: number = 10;

  build() {
    Column() {
      Row() {
        Column() {
          // customCounter必須從父組件初始化,因?yàn)镸yComponent的customCounter成員變量缺少本地初始化夭禽;此處霞掺,customCounter2可以不做初始化。
          MyComponent({ customCounter: this.mainCounter })
          // customCounter2也可以從父組件初始化讹躯,父組件初始化的值會(huì)覆蓋子組件customCounter2的本地初始化的值
          MyComponent({ customCounter: this.mainCounter, customCounter2: this.mainCounter })
        }
      }

      Row() {
        Column() {
          Button('Click to change number')
            .width(288)
            .height(40)
            .margin({ left: 30, top: 12 })
            .fontColor('#FFFFFF菩彬,90%')
            .onClick(() => {
              this.mainCounter++
            })
        }
      }
    }
  }
}

@Prop嵌套場景

在嵌套場景下,每一層都要用@Observed裝飾潮梯,且每一層都要被@Prop接收骗灶,這樣才能觀察到嵌套場景。

// 以下是嵌套類對(duì)象的數(shù)據(jù)結(jié)構(gòu)秉馏。
@Observed
class ClassA {
  public title: string;

  constructor(title: string) {
    this.title = title;
  }
}

@Observed
class ClassB {
  public name: string;
  public a: ClassA;

  constructor(name: string, a: ClassA) {
    this.name = name;
    this.a = a;
  }
}

以下組件層次結(jié)構(gòu)呈現(xiàn)的是@Prop嵌套場景的數(shù)據(jù)結(jié)構(gòu)耙旦。

@Entry
@Component
struct Parent {
  @State votes: ClassB = new ClassB('Hello', new ClassA('world'))

  build() {
    Column() {
      Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center }) {
        Button('change ClassB name')
          .width(312)
          .height(40)
          .margin(12)
          .fontColor('#FFFFFF,90%')
          .onClick(() => {
            this.votes.name = "aaaaa"
          })
        Button('change ClassA title')
          .width(312)
          .height(40)
          .margin(12)
          .fontColor('#FFFFFF沃饶,90%')
          .onClick(() => {
            this.votes.a.title = "wwwww"
          })
        Text(this.votes.name)
          .fontSize(16)
          .margin(12)
          .width(312)
          .height(40)
          .backgroundColor('#ededed')
          .borderRadius(20)
          .textAlign(TextAlign.Center)
          .fontColor('#e6000000')
          .onClick(() => {
            this.votes.name = 'Bye'
          })
        Text(this.votes.a.title)
          .fontSize(16)
          .margin(12)
          .width(312)
          .height(40)
          .backgroundColor('#ededed')
          .borderRadius(20)
          .textAlign(TextAlign.Center)
          .onClick(() => {
            this.votes.a.title = "openHarmony"
          })
        Child1({ vote1: this.votes.a })
      }

    }

  }
}


@Component
struct Child1 {
  @Prop vote1: ClassA = new ClassA('');

  build() {
    Column() {
      Text(this.vote1.title)
        .fontSize(16)
        .margin(12)
        .width(312)
        .height(40)
        .backgroundColor('#ededed')
        .borderRadius(20)
        .textAlign(TextAlign.Center)
        .onClick(() => {
          this.vote1.title = 'Bye Bye'
        })
    }
  }
}

裝飾Map類型變量

說明:

從API version 11開始母廷,@Prop支持Map類型。

在下面的示例中糊肤,value類型為Map<number, string>,點(diǎn)擊Button改變message的值氓鄙,視圖會(huì)隨之刷新馆揉。

@Component
struct Child {
  @Prop value: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]])

  build() {
    Column() {
      ForEach(Array.from(this.value.entries()), (item: [number, string]) => {
        Text(`${item[0]}`).fontSize(30)
        Text(`${item[1]}`).fontSize(30)
        Divider()
      })
      Button('child init map').onClick(() => {
        this.value = new Map([[0, "a"], [1, "b"], [3, "c"]])
      })
      Button('child set new one').onClick(() => {
        this.value.set(4, "d")
      })
      Button('child clear').onClick(() => {
        this.value.clear()
      })
      Button('child replace the first one').onClick(() => {
        this.value.set(0, "aa")
      })
      Button('child delete the first one').onClick(() => {
        this.value.delete(0)
      })
    }
  }
}


@Entry
@Component
struct MapSample2 {
  @State message: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]])

  build() {
    Row() {
      Column() {
        Child({ value: this.message })
      }
      .width('100%')
    }
    .height('100%')
  }
}

裝飾Set類型變量

說明:
從API version 11開始,@Prop支持Set類型抖拦。

在下面的示例中升酣,message類型為Set<number>,點(diǎn)擊Button改變message的值态罪,視圖會(huì)隨之刷新噩茄。

@Component
struct Child {
  @Prop message: Set<number> = new Set([0, 1, 2, 3, 4])

  build() {
    Column() {
      ForEach(Array.from(this.message.entries()), (item: [number, string]) => {
        Text(`${item[0]}`).fontSize(30)
        Divider()
      })
      Button('init set').onClick(() => {
        this.message = new Set([0, 1, 2, 3, 4])
      })
      Button('set new one').onClick(() => {
        this.message.add(5)
      })
      Button('clear').onClick(() => {
        this.message.clear()
      })
      Button('delete the first one').onClick(() => {
        this.message.delete(0)
      })
    }
    .width('100%')
  }
}


@Entry
@Component
struct SetSample11 {
  @State message: Set<number> = new Set([0, 1, 2, 3, 4])

  build() {
    Row() {
      Column() {
        Child({ message: this.message })
      }
      .width('100%')
    }
    .height('100%')
  }
}

Prop支持聯(lián)合類型實(shí)例

@Prop支持聯(lián)合類型和undefined和null,在下面的示例中复颈,animal類型為Animals | undefined绩聘,點(diǎn)擊父組件Zoo中的Button改變animal的屬性或者類型,Child中也會(huì)對(duì)應(yīng)刷新。

class Animals {
  public name: string;

  constructor(name: string) {
    this.name = name;
  }
}

@Component
struct Child {
  @Prop animal: Animals | undefined;

  build() {
    Column() {
      Text(`Child's animal is  ${this.animal instanceof Animals ? this.animal.name : 'undefined'}`).fontSize(30)

      Button('Child change animals into tigers')
        .onClick(() => {
          // 賦值為Animals的實(shí)例
          this.animal = new Animals("Tiger")
        })

      Button('Child change animal to undefined')
        .onClick(() => {
          // 賦值為undefined
          this.animal = undefined
        })

    }.width('100%')
  }
}

@Entry
@Component
struct Zoo {
  @State animal: Animals | undefined = new Animals("lion");

  build() {
    Column() {
      Text(`Parents' animals are  ${this.animal instanceof Animals ? this.animal.name : 'undefined'}`).fontSize(30)

      Child({animal: this.animal})

      Button('Parents change animals into dogs')
        .onClick(() => {
          // 判斷animal的類型凿菩,做屬性的更新
          if (this.animal instanceof Animals) {
            this.animal.name = "Dog"
          } else {
            console.info('num is undefined, cannot change property')
          }
        })

      Button('Parents change animal to undefined')
        .onClick(() => {
          // 賦值為undefined
          this.animal = undefined
        })
    }
  }
}

常見問題

@Prop裝飾狀態(tài)變量未初始化錯(cuò)誤

@Prop需要被初始化机杜,如果沒有進(jìn)行本地初始化的,則必須通過父組件進(jìn)行初始化衅谷。如果進(jìn)行了本地初始化椒拗,那么是可以不通過父組件進(jìn)行初始化的。

【反例】

@Observed
class Commodity {
  public price: number = 0;

  constructor(price: number) {
    this.price = price;
  }
}

@Component
struct PropChild {
  @Prop fruit: Commodity; // 未進(jìn)行本地初始化

  build() {
    Text(`PropChild fruit ${this.fruit.price}`)
      .onClick(() => {
        this.fruit.price += 1;
      })
  }
}

@Entry
@Component
struct Parent {
  @State fruit: Commodity[] = [new Commodity(1)];

  build() {
    Column() {
      Text(`Parent fruit ${this.fruit[0].price}`)
        .onClick(() => {
          this.fruit[0].price += 1;
        })

      // @Prop本地沒有初始化获黔,也沒有從父組件初始化
      PropChild()
    }
  }
}

【正例】

@Observed
class Commodity {
  public price: number = 0;

  constructor(price: number) {
    this.price = price;
  }
}

@Component
struct PropChild1 {
  @Prop fruit: Commodity; // 未進(jìn)行本地初始化

  build() {
    Text(`PropChild1 fruit ${this.fruit.price}`)
      .onClick(() => {
        this.fruit.price += 1;
      })
  }
}

@Component
struct PropChild2 {
  @Prop fruit: Commodity = new Commodity(1); // 進(jìn)行本地初始化

  build() {
    Text(`PropChild2 fruit ${this.fruit.price}`)
      .onClick(() => {
        this.fruit.price += 1;
      })
  }
}

@Entry
@Component
struct Parent {
  @State fruit: Commodity[] = [new Commodity(1)];

  build() {
    Column() {
      Text(`Parent fruit ${this.fruit[0].price}`)
        .onClick(() => {
          this.fruit[0].price += 1;
        })

      // @PropChild1本地沒有初始化蚀苛,必須從父組件初始化
      PropChild1({ fruit: this.fruit[0] })
      // @PropChild2本地進(jìn)行了初始化,可以不從父組件初始化玷氏,也可以從父組件初始化
      PropChild2()
      PropChild2({ fruit: this.fruit[0] })
    }
  }
}

寫在最后

  • 如果你覺得這篇內(nèi)容對(duì)你還蠻有幫助堵未,我想邀請(qǐng)你幫我三個(gè)小忙:
  • 點(diǎn)贊,轉(zhuǎn)發(fā)预茄,有你們的 『點(diǎn)贊和評(píng)論』兴溜,才是我創(chuàng)造的動(dòng)力。
  • 關(guān)注小編耻陕,同時(shí)可以期待后續(xù)文章ing??拙徽,不定期分享原創(chuàng)知識(shí)。
  • 想要獲取更多完整鴻蒙最新學(xué)習(xí)知識(shí)點(diǎn)诗宣,請(qǐng)移步前往小編:https://gitee.com/MNxiaona/733GH/blob/master/jianshu
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末膘怕,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子召庞,更是在濱河造成了極大的恐慌岛心,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,686評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件篮灼,死亡現(xiàn)場離奇詭異忘古,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)诅诱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,668評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門髓堪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人娘荡,你說我怎么就攤上這事干旁。” “怎么了炮沐?”我有些...
    開封第一講書人閱讀 158,160評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵争群,是天一觀的道長。 經(jīng)常有香客問我大年,道長换薄,這世上最難降的妖魔是什么玉雾? 我笑而不...
    開封第一講書人閱讀 56,736評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮专控,結(jié)果婚禮上抹凳,老公的妹妹穿的比我還像新娘。我一直安慰自己伦腐,他們只是感情好赢底,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,847評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著柏蘑,像睡著了一般幸冻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上咳焚,一...
    開封第一講書人閱讀 50,043評(píng)論 1 291
  • 那天洽损,我揣著相機(jī)與錄音,去河邊找鬼革半。 笑死碑定,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的又官。 我是一名探鬼主播延刘,決...
    沈念sama閱讀 39,129評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼六敬!你這毒婦竟也來了碘赖?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,872評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤外构,失蹤者是張志新(化名)和其女友劉穎普泡,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體审编,經(jīng)...
    沈念sama閱讀 44,318評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡撼班,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,645評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了垒酬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片权烧。...
    茶點(diǎn)故事閱讀 38,777評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖伤溉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情妻率,我是刑警寧澤乱顾,帶...
    沈念sama閱讀 34,470評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站宫静,受9級(jí)特大地震影響走净,放射性物質(zhì)發(fā)生泄漏券时。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,126評(píng)論 3 317
  • 文/蒙蒙 一伏伯、第九天 我趴在偏房一處隱蔽的房頂上張望橘洞。 院中可真熱鬧,春花似錦说搅、人聲如沸炸枣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,861評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽适肠。三九已至,卻和暖如春候引,著一層夾襖步出監(jiān)牢的瞬間侯养,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,095評(píng)論 1 267
  • 我被黑心中介騙來泰國打工澄干, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留逛揩,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,589評(píng)論 2 362
  • 正文 我出身青樓麸俘,卻偏偏與公主長得像辩稽,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子疾掰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,687評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容