在聲明式UI編程框架中,UI是程序狀態(tài)的運(yùn)行結(jié)果,用戶構(gòu)建了一個(gè)UI模型,其中應(yīng)用的運(yùn)行時(shí)的狀態(tài)是參數(shù)。當(dāng)參數(shù)改變時(shí),UI作為返回結(jié)果,也將進(jìn)行對(duì)應(yīng)的改變。這些運(yùn)行時(shí)的狀態(tài)變化所帶來(lái)的UI的重新渲染,在ArkUI中統(tǒng)稱為狀態(tài)管理機(jī)制。
裝飾器總覽
ArkUI提供了多種裝飾器,根據(jù)狀態(tài)變量的影響范圍,將所有的裝飾器可以大致分為:
- 管理組件擁有狀態(tài)的裝飾器:組件級(jí)別的狀態(tài)管理,可以觀察組件內(nèi)變化,和不同組件層級(jí)的變化,但需要同一個(gè)頁(yè)面內(nèi)。
- 管理應(yīng)用擁有狀態(tài)的裝飾器:應(yīng)用級(jí)別的狀態(tài)管理,可以觀察不同頁(yè)面,甚至不同UIAbility的狀態(tài)變化,是應(yīng)用內(nèi)全局的狀態(tài)管理。
![]()
上圖中,Components部分的裝飾器為組件級(jí)別的狀態(tài)管理,Application部分為應(yīng)用的狀態(tài)管理。
管理組件擁有的狀態(tài),即圖中Components級(jí)別的狀態(tài)管理:
@State:@State裝飾的變量擁有其所屬組件的狀態(tài),可以作為其子組件單向和雙向同步的數(shù)據(jù)源。當(dāng)其數(shù)值改變時(shí),會(huì)引起相關(guān)組件的渲染刷新。@Prop:@Prop裝飾的變量可以和父組件建立單向同步關(guān)系,@Prop裝飾的變量是可變的,但修改不會(huì)同步回父組件。@Link:@Link裝飾的變量和父組件構(gòu)建雙向同步關(guān)系的狀態(tài)變量,父組件會(huì)接受來(lái)自@Link裝飾的變量的修改的同步,父組件的更新也會(huì)同步給@Link裝飾的變量。@Provide/@Consume:@Provide/@Consume裝飾的變量用于跨組件層級(jí)(多層組件)同步狀態(tài)變量,可以不需要通過(guò)參數(shù)命名機(jī)制傳遞,通過(guò)alias(別名)或者屬性名綁定。@Observed:@Observed裝飾class,需要觀察多層嵌套場(chǎng)景的class需要被@Observed裝飾。單獨(dú)使用@Observed沒有任何作用,需要和@ObjectLink、@Prop連用。@ObjectLink:@ObjectLink裝飾的變量接收@Observed裝飾的class的實(shí)例,應(yīng)用于觀察多層嵌套場(chǎng)景,和父組件的數(shù)據(jù)源構(gòu)建雙向同步。
管理應(yīng)用擁有的狀態(tài),即圖中Application級(jí)別的狀態(tài)管理:
LocalStorage:頁(yè)面級(jí)UI狀態(tài)存儲(chǔ),通常用于UIAbility內(nèi)、頁(yè)面間的狀態(tài)共享。AppStorage:特殊的單例LocalStorage對(duì)象,由UI框架在應(yīng)用程序啟動(dòng)時(shí)創(chuàng)建,為應(yīng)用程序UI狀態(tài)屬性提供中央存儲(chǔ)。PersistentStorage:持久化存儲(chǔ)UI狀態(tài),通常和AppStorage配合使用,選擇AppStorage存儲(chǔ)的數(shù)據(jù)寫入磁盤,以確保這些屬性在應(yīng)用程序重新啟動(dòng)時(shí)的值與應(yīng)用程序關(guān)閉時(shí)的值相同。Environment:應(yīng)用程序運(yùn)行的設(shè)備的環(huán)境參數(shù),環(huán)境參數(shù)會(huì)同步到AppStorage中,可以和AppStorage搭配使用。
其他狀態(tài)管理功能
@Watch用于監(jiān)聽狀態(tài)變量的變化。$$運(yùn)算符:給內(nèi)置組件提供TS變量的引用,使得TS變量和內(nèi)置組件的內(nèi)部狀態(tài)保持同步。
管理組件擁有的狀態(tài)
@State裝飾器:組件內(nèi)狀態(tài)
@State裝飾的變量,或稱為狀態(tài)變量,一旦變量擁有了狀態(tài)屬性,就和自定義組件的渲染綁定起來(lái)。當(dāng)狀態(tài)改變時(shí),UI會(huì)發(fā)生對(duì)應(yīng)的渲染改變。
@State裝飾的變量擁有以下特點(diǎn):
- @State裝飾的變量與子組件中的@Prop裝飾變量之間建立單向數(shù)據(jù)同步,與@Link、@ObjectLink裝飾變量之間建立雙向數(shù)據(jù)同步。
- @State裝飾的變量生命周期與其所屬自定義組件的生命周期相同。
- 支持多種數(shù)據(jù)類型:允許 Object、class、string、number、boolean、enum、Date類型,以及這些類型的數(shù)組。
- 內(nèi)部私有:標(biāo)記為 @State 的屬性是私有變量,只能在組件內(nèi)訪問(wèn)。
- 支持多個(gè)實(shí)例:組件不同實(shí)例的內(nèi)部狀態(tài)數(shù)據(jù)獨(dú)立。
- 需要本地初始化:必須為所有 @State 變量分配初始值,將變量保持未初始化可能導(dǎo)致框架行為未定義,初始值需要是有意義的值,比如設(shè)置
class類型的值為null就是無(wú)意義的,會(huì)導(dǎo)致編譯報(bào)錯(cuò)。 - 創(chuàng)建自定義組件時(shí)支持通過(guò)狀態(tài)變量名設(shè)置初始值:在創(chuàng)建組件實(shí)例時(shí),可以通過(guò)變量名顯式指定 @State 狀態(tài)屬性的初始值。
裝飾class對(duì)象類型的變量
當(dāng)裝飾的數(shù)據(jù)類型為class或者Object時(shí),可以觀察到自身的賦值的變化,和其屬性賦值的變化,即Object.keys(observedObject)返回的所有屬性
創(chuàng)建一個(gè)Model對(duì)象
class Model {
public value: string;
constructor(value: string) {
this.value = value;
}
}
在父組件中初始化@State裝飾對(duì)象,父組件初始化將會(huì)覆蓋本地初始化。
@Entry
@Component
struct EntryComponent {
build() {
Column() {
// 此處指定的參數(shù)都將在初始渲染時(shí)覆蓋本地定義的默認(rèn)值,并不是所有的參數(shù)都需要從父組件初始化
MyComponent({ count: 1, increaseBy: 2 })
.width(300)
MyComponent({ title: new Model('Hello World 2'), count: 7 })
}
}
}
在本地初始化@State裝飾對(duì)象,@State變量更新會(huì)觸發(fā)組件UI更新
@Component
struct MyComponent {
@State title: Model = new Model('Hello World');
@State count: number = 0;
private increaseBy: number = 1;
build() {
Column() {
Text(`${this.title.value}`)
.margin(10)
Button(`Click to change title`)
.onClick(() => {
// @State變量的更新將觸發(fā)上面的Text組件內(nèi)容更新
this.title.value = this.title.value === 'Hello ArkUI' ? 'Hello World' : 'Hello ArkUI';
})
.width(300)
.margin(10)
Button(`Click to increase count = ${this.count}`)
.onClick(() => {
// @State變量的更新將觸發(fā)該Button組件的內(nèi)容更新
this.count += this.increaseBy;
})
.width(300)
.margin(10)
}
}
}

@Prop裝飾器:父子單向同步
@Prop裝飾的變量可以和父組件建立單向的同步關(guān)系。@Prop裝飾的變量是可變的,但是變化不會(huì)同步回其父組件。
@Prop裝飾的變量擁有以下特點(diǎn):
- 支持簡(jiǎn)單數(shù)據(jù)類型:僅支持
number、string、boolean、Object、class、enum類型; - 內(nèi)部私有:標(biāo)記為
@Prop的屬性是私有變量,只能在組件內(nèi)訪問(wèn)。 - 支持多個(gè)實(shí)例:組件不同實(shí)例的內(nèi)部狀態(tài)數(shù)據(jù)獨(dú)立。
- @Prop裝飾器不能在@Entry裝飾的自定義組件中使用。
@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 })
}
}
}
@Link裝飾器:父子雙向同步
子組件中被@Link裝飾的變量與其父組件中對(duì)應(yīng)的數(shù)據(jù)源建立雙向數(shù)據(jù)綁定。@Link裝飾的變量與其父組件中的數(shù)據(jù)源共享相同的值。
@Link裝飾的變量擁有以下特點(diǎn):
- Object、class、string、number、boolean、enum類型,以及這些類型的數(shù)組。支持Date類型。
- 內(nèi)部私有:標(biāo)記為
@Link的屬性是私有變量,只能在組件內(nèi)訪問(wèn)。 - 支持多個(gè)實(shí)例:組件不同實(shí)例的內(nèi)部狀態(tài)數(shù)據(jù)獨(dú)立。
- 不支持內(nèi)部初始化:在創(chuàng)建組件的新實(shí)例時(shí),必須將值傳遞給
@Link修飾的變量進(jìn)行初始化,不支持在組件內(nèi)部進(jìn)行初始化。
class GreenButtonState {
width: number = 0;
constructor(width: number) {
this.width = width;
}
}
@Component
struct GreenButton {
@Link greenButtonState: GreenButtonState;
build() {
Button('Green Button')
.width(this.greenButtonState.width)
.height(40)
.backgroundColor('#64bb5c')
.fontColor('#FFFFFF,90%')
.onClick(() => {
if (this.greenButtonState.width < 700) {
// 更新class的屬性,變化可以被觀察到同步回父組件
this.greenButtonState.width += 60;
} else {
// 更新class,變化可以被觀察到同步回父組件
this.greenButtonState = new GreenButtonState(180);
}
})
}
}
@Component
struct YellowButton {
@Link yellowButtonState: number;
build() {
Button('Yellow Button')
.width(this.yellowButtonState)
.height(40)
.backgroundColor('#f7ce00')
.fontColor('#FFFFFF,90%')
.onClick(() => {
// 子組件的簡(jiǎn)單類型可以同步回父組件
this.yellowButtonState += 40.0;
})
}
}
@Entry
@Component
struct ShufflingContainer {
@State greenButtonState: GreenButtonState = new GreenButtonState(180);
@State yellowButtonProp: number = 180;
build() {
Column() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center }) {
// 簡(jiǎn)單類型從父組件@State向子組件@Link數(shù)據(jù)同步
Button('Parent View: Set yellowButton')
.width(312)
.height(40)
.margin(12)
.fontColor('#FFFFFF,90%')
.onClick(() => {
this.yellowButtonProp = (this.yellowButtonProp < 700) ? this.yellowButtonProp + 40 : 100;
})
// class類型從父組件@State向子組件@Link數(shù)據(jù)同步
Button('Parent View: Set GreenButton')
.width(312)
.height(40)
.margin(12)
.fontColor('#FFFFFF,90%')
.onClick(() => {
this.greenButtonState.width = (this.greenButtonState.width < 700) ? this.greenButtonState.width + 100 : 100;
})
// class類型初始化@Link
GreenButton({ greenButtonState: $greenButtonState }).margin(12)
// 簡(jiǎn)單類型初始化@Link
YellowButton({ yellowButtonState: $yellowButtonProp }).margin(12)
}
}
}
}
1.點(diǎn)擊子組件GreenButton和YellowButton中的Button,子組件會(huì)發(fā)生相應(yīng)變化,將變化同步給父組件。因?yàn)锧Link是雙向同步,會(huì)將變化同步給@State。
2.當(dāng)點(diǎn)擊父組件ShufflingContainer中的Button時(shí),@State變化,也會(huì)同步給@Link,子組件也會(huì)發(fā)生對(duì)應(yīng)的刷新。
管理應(yīng)用擁有的狀態(tài)
LocalStorage:頁(yè)面級(jí)UI狀態(tài)存儲(chǔ)
LocalStorage是頁(yè)面級(jí)的UI狀態(tài)存儲(chǔ),通過(guò)@Entry裝飾器接收的參數(shù)可以在頁(yè)面內(nèi)共享同一個(gè)LocalStorage實(shí)例。LocalStorage支持UIAbility實(shí)例內(nèi)多個(gè)頁(yè)面間狀態(tài)共享。
LocalStorage是ArkTS為構(gòu)建頁(yè)面級(jí)別狀態(tài)變量提供存儲(chǔ)的內(nèi)存內(nèi)“數(shù)據(jù)庫(kù)”。
- 應(yīng)用程序可以創(chuàng)建多個(gè)LocalStorage實(shí)例,LocalStorage實(shí)例可以在頁(yè)面內(nèi)共享,也可以通過(guò)GetShared接口,實(shí)現(xiàn)跨頁(yè)面、UIAbility實(shí)例內(nèi)共享。
- 組件樹的根節(jié)點(diǎn),即被@Entry裝飾的@Component,可以被分配一個(gè)LocalStorage實(shí)例,此組件的所有子組件實(shí)例將自動(dòng)獲得對(duì)該LocalStorage實(shí)例的訪問(wèn)權(quán)限。
- 被@Component裝飾的組件最多可以訪問(wèn)一個(gè)LocalStorage實(shí)例和AppStorage,未被@Entry裝飾的組件不可被獨(dú)立分配LocalStorage實(shí)例,只能接受父組件通過(guò)@Entry傳遞來(lái)的LocalStorage實(shí)例。一個(gè)LocalStorage實(shí)例在組件樹上可以被分配給多個(gè)組件。
- LocalStorage中的所有屬性都是可變的。
LocalStorage根據(jù)與@Component裝飾的組件的同步類型不同,提供了兩個(gè)裝飾器:
- @LocalStorageProp:@LocalStorageProp裝飾的變量和與LocalStorage中給定屬性建立單向同步關(guān)系。
- @LocalStorageLink:@LocalStorageLink裝飾的變量和在@Component中創(chuàng)建與LocalStorage中給定屬性建立雙向同步關(guān)系。
AppStorage:應(yīng)用全局的UI狀態(tài)存儲(chǔ)
AppStorage是應(yīng)用全局的UI狀態(tài)存儲(chǔ),是和應(yīng)用的進(jìn)程綁定的,由UI框架在應(yīng)用程序啟動(dòng)時(shí)創(chuàng)建,為應(yīng)用程序UI狀態(tài)屬性提供中央存儲(chǔ)。
和AppStorage不同的是,LocalStorage是頁(yè)面級(jí)的,通常應(yīng)用于頁(yè)面內(nèi)的數(shù)據(jù)共享。而AppStorage是應(yīng)用級(jí)的全局狀態(tài)共享,還相當(dāng)于整個(gè)應(yīng)用的“中樞”,持久化數(shù)據(jù)PersistentStorage和環(huán)境變量Environment都是通過(guò)AppStorage中轉(zhuǎn),才可以和UI交互。
@StorageProp
- 單向同步:從AppStorage的對(duì)應(yīng)屬性到組件的狀態(tài)變量。
- Object、 class、string、number、boolean、enum類型,以及這些類型的數(shù)組。
- 組件本地的修改是允許的,但是AppStorage中給定的屬性一旦發(fā)生變化,將覆蓋本地的修改。
- @StorageProp不支持從父節(jié)點(diǎn)初始化,只能AppStorage中key對(duì)應(yīng)的屬性初始化,如果沒有對(duì)應(yīng)key的話,將使用本地默認(rèn)值初始化。
@StorageLink
@StorageLink(key) 裝飾的變量是組件內(nèi)部的狀態(tài)數(shù)據(jù),當(dāng)這些狀態(tài)數(shù)據(jù)被修改時(shí),將會(huì)調(diào)用所在組件的 build() 方法進(jìn)行UI刷新。組件通過(guò)使用 @StorageLink(key) 裝飾的狀態(tài)變量與 AppStorage 建立雙向數(shù)據(jù)綁定。
- 支持多種數(shù)據(jù)類型:支持的數(shù)據(jù)類型和
@State一致且支持object。 - 需要本地初始化:必須為所有
@StorageLink變量分配初始值。 - 數(shù)據(jù)狀態(tài)全局化:使用
@StorageLink修飾的數(shù)據(jù)變化后全局都會(huì)改變。 - 數(shù)據(jù)持久化:通過(guò)搭配
PersistentStorage接口實(shí)現(xiàn)數(shù)據(jù)持久化。
@Entry @Component struct ComponentTest {
@StorageLink('time') time: string = "1648643734154";// 使用StorageLink標(biāo)記并初始化
build() {
Column({space: 10}) {
Text(`父組件【${this.time}】`) // 使用time值
.fontSize(20)
.backgroundColor(Color.Pink)
Button('更新時(shí)間')
.onClick(() => {
this.time = new Date().getTime().toString();// 更改time的值
})
}
.width('100%')
.height('100%')
.padding(10)
}
}
其他狀態(tài)管理
@Watch裝飾器:狀態(tài)變量更改通知
@Watch 用來(lái)監(jiān)聽狀態(tài)變量的變化,當(dāng)它修飾的狀態(tài)變量發(fā)生變更時(shí),回調(diào)相應(yīng)的方式,
- 當(dāng)觀察到狀態(tài)變量的變化的時(shí)候,對(duì)應(yīng)的@Watch的回調(diào)方法將被觸發(fā);
- @Watch方法在自定義組件的屬性變更之后同步執(zhí)行;
- 如果在@Watch的方法里改變了其他的狀態(tài)變量,也會(huì)引起狀態(tài)變更和@Watch的執(zhí)行;
- 在第一次初始化的時(shí)候,@Watch裝飾的方法不會(huì)被調(diào)用,即認(rèn)為初始化不是狀態(tài)變量的改變。只有在后續(xù)狀態(tài)改變時(shí),才會(huì)調(diào)用@Watch回調(diào)方法。
- 為了避免循環(huán)的產(chǎn)生,建議不要在@Watch的回調(diào)方法里修改當(dāng)前裝飾的狀態(tài)變量;
- 不建議在@Watch函數(shù)中調(diào)用async await,異步行為可能會(huì)導(dǎo)致重新渲染速度的性能問(wèn)題。
//給狀態(tài)變量 `count` 增加一個(gè) `@Watch` 裝飾器,通過(guò) `@Watch` 注冊(cè)一個(gè)回調(diào)方法 `function_name`
@State @Watch("function_name") count : number = 0;
//當(dāng)狀態(tài)變量 `count` 被改變時(shí), 觸發(fā) `function_name` 回調(diào)。
function_name(propName: string): void {}
@Watch和自定義組件更新
@Component
struct TotalView {
@Prop @Watch('onCountUpdated') count: number = 0;
@State total: number = 0;
// @Watch 回調(diào)
onCountUpdated(propName: string): void {
this.total += this.count;
}
build() {
Text(`Total: ${this.total}`)
}
}
@Entry
@Component
struct CountModifier {
@State count: number = 0;
build() {
Column() {
Button('add to basket')
.onClick(() => {
this.count++
})
TotalView({ count: this.count })
}
}
}
$$語(yǔ)法:內(nèi)置組件雙向同步
$$運(yùn)算符為系統(tǒng)內(nèi)置組件提供TS變量的引用,使得TS變量和系統(tǒng)內(nèi)置組件的內(nèi)部狀態(tài)保持同步。
以TextInput方法的text參數(shù)為例:
@Entry
@Component
struct TextInputExample {
@State text: string = ''
controller: TextInputController = new TextInputController()
build() {
Column({ space: 20 }) {
Text(this.text)
TextInput({ text: $$this.text, placeholder: 'input your word...', controller: this.controller })
.placeholderColor(Color.Grey)
.placeholderFont({ size: 14, weight: 400 })
.caretColor(Color.Blue)
.width(300)
}.width('100%').height('100%').justifyContent(FlexAlign.Center)
}
}

浙公網(wǎng)安備 33010602011771號(hào)