@Prop裝飾器:父子單向同步
@Prop裝飾的變量可以和父組件建立單向的同步關(guān)系。@Prop裝飾的變量是可變的,但是變化不會(huì)同步回其父組件。
概述
@Prop裝飾的變量和父組件建立單向的同步關(guān)系:
- @Prop變量允許在本地修改,但修改后的變化不會(huì)同步回父組件。
- 當(dāng)父組件中的數(shù)據(jù)源更改時(shí),與之相關(guān)的@Prop裝飾的變量都會(huì)自動(dòng)更新。如果子組件已經(jīng)在本地修改了@Prop裝飾的相關(guān)變量值,而在父組件中對(duì)應(yīng)的@State裝飾的變量被修改后,子組件本地修改的@Prop裝飾的相關(guān)變量值將被覆蓋。
裝飾器使用規(guī)則說(shuō)明
@Prop變量裝飾器 |
說(shuō)明 |
---|---|
裝飾器參數(shù) |
無(wú) |
同步類(lèi)型 |
單向同步:對(duì)父組件狀態(tài)變量值的修改,將同步給子組件@Prop裝飾的變量,子組件@Prop變量的修改不會(huì)同步到父組件的狀態(tài)變量上 |
允許裝飾的變量類(lèi)型 |
string、number、boolean、enum類(lèi)型。 不支持any,不允許使用undefined和null。 必須指定類(lèi)型。 在父組件中,傳遞給@Prop裝飾的值不能為undefined或者null,反例如下所示。 CompA ({ aProp: undefined }) CompA ({ aProp: null }) @Prop和數(shù)據(jù)源類(lèi)型需要相同,有以下三種情況(數(shù)據(jù)源以@State為例):
|
被裝飾變量的初始值 |
允許本地初始化。 |
變量的傳遞/訪問(wèn)規(guī)則說(shuō)明
傳遞/訪問(wèn) |
說(shuō)明 |
---|---|
從父組件初始化 |
如果本地有初始化,則是可選的。沒(méi)有的話,則必選,支持父組件中的常規(guī)變量、@State、@Link、@Prop、@Provide、@Consume、@ObjectLink、@StorageLink、@StorageProp、@LocalStorageLink和@LocalStorageProp去初始化子組件中的@Prop變量。 |
用于初始化子組件 |
@Prop支持去初始化子組件中的常規(guī)變量、@State、@Link、@Prop、@Provide。 |
是否支持組件外訪問(wèn) |
@Prop裝飾的變量是私有的,只能在組件內(nèi)訪問(wèn)。 |
圖1?初始化規(guī)則圖示
?
觀察變化和行為表現(xiàn)
觀察變化
@Prop裝飾的數(shù)據(jù)可以觀察到以下變化。
- 當(dāng)裝飾的類(lèi)型是允許的類(lèi)型,即string、number、boolean、enum類(lèi)型都可以觀察到的賦值變化;
// 簡(jiǎn)單類(lèi)型
@Prop count: number;
// 賦值的變化可以被觀察到
this.count = 1;
對(duì)于@State和@Prop的同步場(chǎng)景:
- 使用父組件中@State變量的值初始化子組件中的@Prop變量。當(dāng)@State變量變化時(shí),該變量值也會(huì)同步更新至@Prop變量。
- @Prop裝飾的變量的修改不會(huì)影響其數(shù)據(jù)源@State裝飾變量的值。
- 除了@State,數(shù)據(jù)源也可以用@Link或@Prop裝飾,對(duì)@Prop的同步機(jī)制是相同的。
- 數(shù)據(jù)源和@Prop變量的類(lèi)型需要相同。
框架行為
要理解@Prop變量值初始化和更新機(jī)制,有必要了解父組件和擁有@Prop變量的子組件初始渲染和更新流程。
- 初始渲染:
- 執(zhí)行父組件的build()函數(shù)將創(chuàng)建子組件的新實(shí)例,將數(shù)據(jù)源傳遞給子組件;
- 初始化子組件@Prop裝飾的變量。
- 更新:
- 子組件@Prop更新時(shí),更新僅停留在當(dāng)前子組件,不會(huì)同步回父組件;
- 當(dāng)父組件的數(shù)據(jù)源更新時(shí),子組件的@Prop裝飾的變量將被來(lái)自父組件的數(shù)據(jù)源重置,所有@Prop裝飾的本地的修改將被父組件的更新覆蓋。
使用場(chǎng)景
父組件@State到子組件@Prop簡(jiǎn)單數(shù)據(jù)類(lèi)型同步
以下示例是@State到子組件@Prop簡(jiǎn)單數(shù)據(jù)同步,父組件ParentComponent的狀態(tài)變量countDownStartValue初始化子組件CountDownComponent中@Prop裝飾的count,點(diǎn)擊“Try again”,count的修改僅保留在CountDownComponent 不會(huì)同步給父組件CountDownComponent。
ParentComponent的狀態(tài)變量countDownStartValue的變化將重置CountDownComponent的count。
@Component
struct CountDownComponent {
@Prop count: number;
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 })
}
}
}
在上面的示例中:
- CountDownComponent子組件首次創(chuàng)建時(shí)其@Prop裝飾的count變量將從父組件@State裝飾的countDownStartValue變量初始化;
- 按“+1”或“-1”按鈕時(shí),父組件的@State裝飾的countDownStartValue值會(huì)變化,這將觸發(fā)父組件重新渲染,在父組件重新渲染過(guò)程中會(huì)刷新使用countDownStartValue狀態(tài)變量的UI組件并單向同步更新CountDownComponent子組件中的count值;
- 更新count狀態(tài)變量值也會(huì)觸發(fā)CountDownComponent的重新渲染,在重新渲染過(guò)程中,評(píng)估使用count狀態(tài)變量的if語(yǔ)句條件(this.count > 0),并執(zhí)行true分支中的使用count狀態(tài)變量的UI組件相關(guān)描述來(lái)更新Text組件的UI顯示;
- 當(dāng)按下子組件CountDownComponent的“Try again”按鈕時(shí),其@Prop變量count將被更改,但是count值的更改不會(huì)影響父組件的countDownStartValue值;
- 父組件的countDownStartValue值會(huì)變化時(shí),父組件的修改將覆蓋掉子組件CountDownComponent中count本地的修改。
父組件@State數(shù)組項(xiàng)到子組件@Prop簡(jiǎn)單數(shù)據(jù)類(lèi)型同步
父組件中@State如果裝飾的數(shù)組,其數(shù)組項(xiàng)也可以初始化@Prop。以下示例中父組件Index中@State裝飾的數(shù)組arr,將其數(shù)組項(xiàng)初始化子組件Child中@Prop裝飾的value。
@Component
struct Child {
@Prop value: number;
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 => {
Child({value: item})
},
item => 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ì)更改局部變量值。
假設(shè)我們點(diǎn)擊了多次,所有變量的本地取值都是“7”。
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]})的情況也類(lèi)似。
- this.arr的更改觸發(fā)ForEach更新,this.arr更新的前后都有數(shù)值為3的數(shù)組項(xiàng):[3, 4, 5] 和[1, 2, 3]。根據(jù)diff機(jī)制,數(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類(lèi)對(duì)象屬性到@Prop簡(jiǎn)單類(lèi)型的同步
如果圖書(shū)館有一本圖書(shū)和兩位用戶,每位用戶都可以將圖書(shū)標(biāo)記為已讀,此標(biāo)記行為不會(huì)影響其它讀者用戶。從代碼角度講,對(duì)@Prop圖書(shū)對(duì)象的本地更改不會(huì)同步給圖書(shū)館組件中的@State圖書(shū)對(duì)象。
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 title: string;
@Prop readIt: boolean;
build() {
Row() {
Text(this.title)
Text(`... ${this.readIt ? 'I have read' : 'I have not read it'}`)
.onClick(() => this.readIt = true)
}
}
}
@Entry
@Component
struct Library {
@State book: Book = new Book('100 secrets of C++', 765);
build() {
Column() {
ReaderComp({ title: this.book.title, readIt: this.book.readIt })
ReaderComp({ title: this.book.title, readIt: this.book.readIt })
}
}
}
@Prop本地初始化不和父組件同步
為了支持@Component裝飾的組件復(fù)用場(chǎng)景,@Prop支持本地初始化,這樣可以讓@Prop是否與父組件建立同步關(guān)系變得可選。當(dāng)且僅當(dāng)@Prop有本地初始化時(shí),從父組件向子組件傳遞@Prop的數(shù)據(jù)源才是可選的。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-619631.html
下面的示例中,子組件包含兩個(gè)@Prop變量:文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-619631.html
- @Prop customCounter沒(méi)有本地初始化,所以需要父組件提供數(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}`).width(90).height(40).fontColor('#FF0010')
}
Row() {
Button('Click to change locally !').width(480).height(60).margin({ top: 10 })
.onClick(() => {
this.customCounter2++
})
}.height(100).width(480)
Row() {
Text(`Custom Local: ${this.customCounter2}`).width(90).height(40).fontColor('#FF0010')
}
}
}
}
@Entry
@Component
struct MainProgram {
@State mainCounter: number = 10;
build() {
Column() {
Row() {
Column() {
Button('Click to change number').width(480).height(60).margin({ top: 10, bottom: 10 })
.onClick(() => {
this.mainCounter++
})
}
}
Row() {
Column()
// customCounter必須從父組件初始化,因?yàn)镸yComponent的customCounter成員變量缺少本地初始化;此處,customCounter2可以不做初始化。
MyComponent({ customCounter: this.mainCounter })
// customCounter2也可以從父組件初始化,父組件初始化的值會(huì)覆蓋子組件customCounter2的本地初始化的值
MyComponent({ customCounter: this.mainCounter, customCounter2: this.mainCounter })
}.width('40%')
}
}
}
到了這里,關(guān)于HarmonyOS學(xué)習(xí)路之方舟開(kāi)發(fā)框架—學(xué)習(xí)ArkTS語(yǔ)言(狀態(tài)管理 二)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!