在聲明式UI編程框架中滞磺,UI是程序狀態(tài)的運(yùn)行結(jié)果,用戶構(gòu)建了一個UI模型涵卵,其中應(yīng)用的運(yùn)行時的狀態(tài)是參數(shù)浴栽。當(dāng)參數(shù)改變時,UI作為返回結(jié)果轿偎,也將進(jìn)行對應(yīng)的改變典鸡。這些運(yùn)行時的狀態(tài)變化所帶來的UI的重新渲染,在ArkUI中統(tǒng)稱為狀態(tài)管理機(jī)制坏晦。
自定義組件擁有變量萝玷,變量必須被裝飾器裝飾才可以成為狀態(tài)變量嫁乘,狀態(tài)變量的改變會引起UI的渲染刷新。如果不使用狀態(tài)變量球碉,UI只能在初始化時渲染蜓斧,后續(xù)將不會再刷新。 下圖展示了State和View(UI)之間的關(guān)系睁冬。
- View(UI):UI渲染挎春,指將build方法內(nèi)的UI描述和@Builder裝飾的方法內(nèi)的UI描述映射到界面。
- State:狀態(tài)豆拨,指驅(qū)動UI更新的數(shù)據(jù)搂蜓。用戶通過觸發(fā)組件的事件方法,改變狀態(tài)數(shù)據(jù)辽装。狀態(tài)數(shù)據(jù)的改變,引起UI的重新渲染相味。
基本概念
- 狀態(tài)變量:被狀態(tài)裝飾器裝飾的變量拾积,狀態(tài)變量值的改變會引起UI的渲染更新。示例:@State num: number = 1,其中丰涉,@State是狀態(tài)裝飾器拓巧,num是狀態(tài)變量。
- 常規(guī)變量:沒有被狀態(tài)裝飾器裝飾的變量一死,通常應(yīng)用于輔助計算肛度。它的改變永遠(yuǎn)不會引起UI的刷新。以下示例中increaseBy變量為常規(guī)變量投慈。
- 數(shù)據(jù)源/同步源:狀態(tài)變量的原始來源承耿,可以同步給不同的狀態(tài)數(shù)據(jù)。通常意義為父組件傳給子組件的數(shù)據(jù)伪煤。以下示例中數(shù)據(jù)源為count: 1加袋。
- 命名參數(shù)機(jī)制:父組件通過指定參數(shù)傳遞給子組件的狀態(tài)變量,為父子傳遞同步參數(shù)的主要手段抱既。示例:CompA: ({ aProp: this.aProp })职烧。
- 從父組件初始化:父組件使用命名參數(shù)機(jī)制,將指定參數(shù)傳遞給子組件防泵。子組件初始化的默認(rèn)值在有父組件傳值的情況下蚀之,會被覆蓋。示例:
@Component
struct MyComponent {
@State count: number = 0;
private increaseBy: number = 1;
build() {
}
}
@Component
struct Parent {
build() {
Column() {
// 從父組件初始化捷泞,覆蓋本地定義的默認(rèn)值
MyComponent({ count: 1, increaseBy: 2 })
}
}
}
- 初始化子節(jié)點(diǎn):父組件中狀態(tài)變量可以傳遞給子組件足删,初始化子組件對應(yīng)的狀態(tài)變量。示例同上肚邢。
- 本地初始化:在變量聲明的時候賦值壹堰,作為變量的默認(rèn)值拭卿。示例:@State count: number = 0。
裝飾器總覽
ArkUI提供了多種裝飾器贱纠,通過使用這些裝飾器峻厚,狀態(tài)變量不僅可以觀察在組件內(nèi)的改變,還可以在不同組件層級間傳遞谆焊,比如父子組件惠桃、跨組件層級,也可以觀察全局范圍內(nèi)的變化辖试。根據(jù)狀態(tài)變量的影響范圍辜王,將所有的裝飾器可以大致分為:
- 管理組件擁有狀態(tài)的裝飾器:組件級別的狀態(tài)管理,可以觀察組件內(nèi)變化罐孝,和不同組件層級的變化呐馆,但需要唯一觀察同一個組件樹上,即同一個頁面內(nèi)莲兢。
- 管理應(yīng)用擁有狀態(tài)的裝飾器:應(yīng)用級別的狀態(tài)管理汹来,可以觀察不同頁面,甚至不同UIAbility的狀態(tài)變化改艇,是應(yīng)用內(nèi)全局的狀態(tài)管理收班。
從數(shù)據(jù)的傳遞形式和同步類型層面看,裝飾器也可分為:
- 只讀的單向傳遞谒兄;
- 可變更的雙向傳遞摔桦。
圖示如下,具體裝飾器的介紹承疲,可詳見管理組件擁有的狀態(tài)和管理應(yīng)用擁有的狀態(tài)邻耕。開發(fā)者可以靈活地利用這些能力來實(shí)現(xiàn)數(shù)據(jù)和UI的聯(lián)動。
上圖中纪隙,Components部分的裝飾器為組件級別的狀態(tài)管理赊豌,Application部分為應(yīng)用的狀態(tài)管理。開發(fā)者可以通過@StorageLink/@LocalStorageLink實(shí)現(xiàn)應(yīng)用和組件狀態(tài)的雙向同步绵咱,通過@StorageProp/@LocalStorageProp實(shí)現(xiàn)應(yīng)用和組件狀態(tài)的單向同步碘饼。
管理組件擁有的狀態(tài),即圖中Components級別的狀態(tài)管理:
- @State:@State裝飾的變量擁有其所屬組件的狀態(tài)悲伶,可以作為其子組件單向和雙向同步的數(shù)據(jù)源艾恼。當(dāng)其數(shù)值改變時,會引起相關(guān)組件的渲染刷新麸锉。
- @Prop:@Prop裝飾的變量可以和父組件建立單向同步關(guān)系钠绍,@Prop裝飾的變量是可變的,但修改不會同步回父組件花沉。
- @Link:@Link裝飾的變量和父組件構(gòu)建雙向同步關(guān)系的狀態(tài)變量柳爽,父組件會接受來自@Link裝飾的變量的修改的同步媳握,父組件的更新也會同步給@Link裝飾的變量。
- @Provide/@Consume:@Provide/@Consume裝飾的變量用于跨組件層級(多層組件)同步狀態(tài)變量磷脯,可以不需要通過參數(shù)命名機(jī)制傳遞蛾找,通過alias(別名)或者屬性名綁定。
- @Observed:@Observed裝飾class赵誓,需要觀察多層嵌套場景的class需要被@Observed裝飾打毛。單獨(dú)使用@Observed沒有任何作用,需要和@ObjectLink俩功、@Prop連用幻枉。
- @ObjectLink:@ObjectLink裝飾的變量接收@Observed裝飾的class的實(shí)例,應(yīng)用于觀察多層嵌套場景诡蜓,和父組件的數(shù)據(jù)源構(gòu)建雙向同步熬甫。
說明
僅[@Observed/@ObjectLink]可以觀察嵌套場景,其他的狀態(tài)變量僅能觀察第一層蔓罚,
詳情見各個裝飾器章節(jié)的“觀察變化和行為表現(xiàn)”小節(jié)罗珍。
管理應(yīng)用擁有的狀態(tài),即圖中Application級別的狀態(tài)管理:
- AppStorage是應(yīng)用程序中的一個特殊的單例LocalStorage對象脚粟,是應(yīng)用級的數(shù)據(jù)庫,和進(jìn)程綁定蘸朋,通過@StorageProp和@StorageLink裝飾器可以和組件聯(lián)動核无。
- AppStorage是應(yīng)用狀態(tài)的“中樞”,將需要與組件(UI)交互的數(shù)據(jù)存入AppStorage藕坯,比如持久化數(shù)據(jù)PersistentStorage和環(huán)境變量Environment团南。UI再通過AppStorage提供的裝飾器或者API接口,訪問這些數(shù)據(jù)炼彪。
- 框架還提供了LocalStorage吐根,AppStorage是LocalStorage特殊的單例。LocalStorage是應(yīng)用程序聲明的應(yīng)用狀態(tài)的內(nèi)存“數(shù)據(jù)庫”辐马,通常用于頁面級的狀態(tài)共享拷橘,通過@LocalStorageProp和@LocalStorageLink裝飾器可以和UI聯(lián)動。
其他狀態(tài)管理功能
@Watch用于監(jiān)聽狀態(tài)變量的變化喜爷。
$$運(yùn)算符:給內(nèi)置組件提供TS變量的引用冗疮,使得TS變量和內(nèi)置組件的內(nèi)部狀態(tài)保持同步。
@State裝飾器:組件內(nèi)狀態(tài)
@State裝飾的變量檩帐,與聲明式范式中的其他被裝飾變量一樣术幔,是私有的,只能從組件內(nèi)部訪問湃密,在聲明時必須指定其類型和本地初始化诅挑。初始化也可選擇使用命名參數(shù)機(jī)制從父組件完成初始化四敞。
@State裝飾的變量擁有以下特點(diǎn):
@State裝飾的變量與子組件中的@Prop裝飾變量之間建立單向數(shù)據(jù)同步,與@Link拔妥、@ObjectLink裝飾變量之間建立雙向數(shù)據(jù)同步忿危。
@State裝飾的變量生命周期與其所屬自定義組件的生命周期相同。
class Model {
public value: string;
constructor(value: string) {
this.value = value;
}
}
@Entry
@Component
struct EntryComponent {
build() {
Column() {
// 此處指定的參數(shù)都將在初始渲染時覆蓋本地定義的默認(rèn)值毒嫡,并不是所有的參數(shù)都需要從父組件初始化
MyComponent({ count: 1, increaseBy: 2 })
.width(300)
MyComponent({ title: new Model('Hello World 2'), count: 7 })
}
}
}
@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變量允許在本地修改癌蚁,但修改后的變化不會同步回父組件。
當(dāng)父組件中的數(shù)據(jù)源更改時兜畸,與之相關(guān)的@Prop裝飾的變量都會自動更新努释。如果子組件已經(jīng)在本地修改了@Prop裝飾的相關(guān)變量值,而在父組件中對應(yīng)的@State裝飾的變量被修改后咬摇,子組件本地修改的@Prop裝飾的相關(guān)變量值將被覆蓋伐蒂。
@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裝飾的變量不會同步給父組件
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ù)源的修改會同步給子組件
Button(`+1 - Nuggets in New Game`).onClick(() => {
this.countDownStartValue += 1;
})
// 父組件的修改會同步給子組件
Button(`-1 - Nuggets in New Game`).onClick(() => {
this.countDownStartValue -= 1;
})
CountDownComponent({ count: this.countDownStartValue, costOfOneAttempt: 2 })
}
}
}
@Link裝飾器:父子雙向同步
@Link裝飾的變量與其父組件中的數(shù)據(jù)源共享相同的值。
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(() => {
// 子組件的簡單類型可以同步回父組件
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 }) {
// 簡單類型從父組件@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)
// 簡單類型初始化@Link
YellowButton({ yellowButtonState: $yellowButtonProp }).margin(12)
}
}
}
}
@Provide裝飾器和@Consume裝飾器:與后代組件雙向同步
@Provide和@Consume芒珠,應(yīng)用于與后代組件的雙向數(shù)據(jù)同步桥狡,應(yīng)用于狀態(tài)數(shù)據(jù)在多個層級之間傳遞的場景。不同于上文提到的父子組件之間通過命名參數(shù)機(jī)制傳遞皱卓,@Provide和@Consume擺脫參數(shù)傳遞機(jī)制的束縛裹芝,實(shí)現(xiàn)跨層級傳遞。
其中@Provide裝飾的變量是在祖先節(jié)點(diǎn)中娜汁,可以理解為被“提供”給后代的狀態(tài)變量嫂易。@Consume裝飾的變量是在后代組件中,去“消費(fèi)(綁定)”祖先節(jié)點(diǎn)提供的變量掐禁。
@Provide/@Consume裝飾的狀態(tài)變量有以下特性:
@Provide裝飾的狀態(tài)變量自動對其所有后代組件可用怜械,即該變量被“provide”給他的后代組件。由此可見傅事,@Provide的方便之處在于宫盔,開發(fā)者不需要多次在組件之間傳遞變量。
后代通過使用@Consume去獲取@Provide提供的變量享完,建立在@Provide和@Consume之間的雙向數(shù)據(jù)同步灼芭,與@State/@Link不同的是,前者可以在多層級的父子組件之間傳遞般又。
@Provide和@Consume可以通過相同的變量名或者相同的變量別名綁定彼绷,變量類型必須相同巍佑。
@Component
struct CompD {
// @Consume裝飾的變量通過相同的屬性名綁定其祖先組件CompA內(nèi)的@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()
}
}
}
@Observed裝飾器和@ObjectLink裝飾器:嵌套類對象屬性變化
上文所述的裝飾器僅能觀察到第一層的變化,但是在實(shí)際應(yīng)用開發(fā)中寄悯,應(yīng)用會根據(jù)開發(fā)需要萤衰,封裝自己的數(shù)據(jù)模型。對于多層嵌套的情況猜旬,比如二維數(shù)組脆栋,或者數(shù)組項(xiàng)class,或者class的屬性是class洒擦,他們的第二層的屬性變化是無法觀察到的椿争。這就引出了@Observed/@ObjectLink裝飾器。
@ObjectLink和@Observed類裝飾器用于在涉及嵌套對象或數(shù)組的場景中進(jìn)行雙向數(shù)據(jù)同步:
被@Observed裝飾的類熟嫩,可以被觀察到屬性的變化秦踪;
子組件中@ObjectLink裝飾器裝飾的狀態(tài)變量用于接收@Observed裝飾的類的實(shí)例,和父組件中對應(yīng)的狀態(tài)變量建立雙向數(shù)據(jù)綁定掸茅。這個實(shí)例可以是數(shù)組中的被@Observed裝飾的項(xiàng)椅邓,或者是class object中的屬性,這個屬性同樣也需要被@Observed裝飾昧狮。
單獨(dú)使用@Observed是沒有任何作用的景馁,需要搭配@ObjectLink或者@Prop使用。
@Observed
class StringArray extends Array<String> {
}
@Component
struct ItemPage {
@ObjectLink itemArr: StringArray;
build() {
Row() {
Text('ItemPage')
.width(100).height(100)
ForEach(this.itemArr,
item => {
Text(item)
.width(100).height(100)
},
item => item
)
}
}
}
@Entry
@Component
struct IndexPage {
@State arr: Array<StringArray> = [new StringArray(), new StringArray(), new StringArray()];
build() {
Column() {
ItemPage({ itemArr: this.arr[0] })
ItemPage({ itemArr: this.arr[1] })
ItemPage({ itemArr: this.arr[2] })
Divider()
ForEach(this.arr,
itemArr => {
ItemPage({ itemArr: itemArr })
},
itemArr => itemArr[0]
)
Divider()
Button('update')
.onClick(() => {
console.error('Update all items in arr');
if (this.arr[0][0] !== undefined) {
// 正常情況下需要有一個真實(shí)的ID來與ForEach一起使用逗鸣,但此處沒有
// 因此需要確保推送的字符串是唯一的裁僧。
this.arr[0].push(`${this.arr[0].slice(-1).pop()}${this.arr[0].slice(-1).pop()}`);
this.arr[1].push(`${this.arr[1].slice(-1).pop()}${this.arr[1].slice(-1).pop()}`);
this.arr[2].push(`${this.arr[2].slice(-1).pop()}${this.arr[2].slice(-1).pop()}`);
} else {
this.arr[0].push('Hello');
this.arr[1].push('World');
this.arr[2].push('!');
}
})
}
}
}
上文中介紹的裝飾器僅能在頁面內(nèi),即一個組件樹上共享狀態(tài)變量慕购。如果開發(fā)者要實(shí)現(xiàn)應(yīng)用級的,或者多個頁面的狀態(tài)數(shù)據(jù)共享茬底,就需要用到應(yīng)用級別的狀態(tài)管理的概念沪悲。ArkTS根據(jù)不同特性,提供了多種應(yīng)用狀態(tài)管理的能力:
- LocalStorage:頁面級UI狀態(tài)存儲阱表,通常用于UIAbility內(nèi)殿如、頁面間的狀態(tài)共享。
- AppStorage:特殊的單例LocalStorage對象最爬,由UI框架在應(yīng)用程序啟動時創(chuàng)建涉馁,為應(yīng)用程序UI狀態(tài)屬性提供中央存儲;
- PersistentStorage:持久化存儲UI狀態(tài)爱致,通常和AppStorage配合使用烤送,選擇AppStorage存儲的數(shù)據(jù)寫入磁盤,以確保這些屬性在應(yīng)用程序重新啟動時的值與應(yīng)用程序關(guān)閉時的值相同糠悯;
- Environment:應(yīng)用程序運(yùn)行的設(shè)備的環(huán)境參數(shù)帮坚,環(huán)境參數(shù)會同步到AppStorage中妻往,可以和AppStorage搭配使用。
LocalStorage:頁面級UI狀態(tài)存儲
LocalStorage是頁面級的UI狀態(tài)存儲试和,通過@Entry裝飾器接收的參數(shù)可以在頁面內(nèi)共享同一個LocalStorage實(shí)例讯泣。LocalStorage也可以在UIAbility實(shí)例內(nèi),在頁面間共享狀態(tài)阅悍。
LocalStorage是ArkTS為構(gòu)建頁面級別狀態(tài)變量提供存儲的內(nèi)存內(nèi)“數(shù)據(jù)庫”好渠。
- 應(yīng)用程序可以創(chuàng)建多個LocalStorage實(shí)例,LocalStorage實(shí)例可以在頁面內(nèi)共享节视,也可以通過GetShared接口拳锚,實(shí)現(xiàn)跨頁面、UIAbility實(shí)例內(nèi)共享肴茄。
- 組件樹的根節(jié)點(diǎn)晌畅,即被@Entry裝飾的@Component,可以被分配一個LocalStorage實(shí)例寡痰,此組件的所有子組件實(shí)例將自動獲得對該LocalStorage實(shí)例的訪問權(quán)限抗楔;
- 被@Component裝飾的組件最多可以訪問一個LocalStorage實(shí)例和AppStorage,未被@Entry裝飾的組件不可被獨(dú)立分配LocalStorage實(shí)例拦坠,只能接受父組件通過@Entry傳遞來的LocalStorage實(shí)例连躏。一個LocalStorage實(shí)例在組件樹上可以被分配給多個組件。
- LocalStorage中的所有屬性都是可變的贞滨。
應(yīng)用程序決定LocalStorage對象的生命周期入热。當(dāng)應(yīng)用釋放最后一個指向LocalStorage的引用時,比如銷毀最后一個自定義組件晓铆,LocalStorage將被JS Engine垃圾回收勺良。
LocalStorage根據(jù)與@Component裝飾的組件的同步類型不同,提供了兩個裝飾器:
- @LocalStorageProp:@LocalStorageProp裝飾的變量和與LocalStorage中給定屬性建立單向同步關(guān)系骄噪。
- @LocalStorageLink:@LocalStorageLink裝飾的變量和在@Component中創(chuàng)建與LocalStorage中給定屬性建立雙向同步關(guān)系尚困。
AppStorage:應(yīng)用全局的UI狀態(tài)存儲
AppStorage是應(yīng)用全局的UI狀態(tài)存儲,是和應(yīng)用的進(jìn)程綁定的链蕊,由UI框架在應(yīng)用程序啟動時創(chuàng)建事甜,為應(yīng)用程序UI狀態(tài)屬性提供中央存儲。
和AppStorage不同的是滔韵,LocalStorage是頁面級的逻谦,通常應(yīng)用于頁面內(nèi)的數(shù)據(jù)共享。而AppStorage是應(yīng)用級的全局狀態(tài)共享陪蜻,還相當(dāng)于整個應(yīng)用的“中樞”邦马,持久化數(shù)據(jù)PersistentStorage和環(huán)境變量Environment都是通過和AppStorage中轉(zhuǎn),才可以和UI交互。
本文僅介紹AppStorage使用場景和相關(guān)的裝飾器:@StorageProp和@StorageLink勇婴。
AppStorage是在應(yīng)用啟動的時候會被創(chuàng)建的單例忱嘹。它的目的是為了提供應(yīng)用狀態(tài)數(shù)據(jù)的中心存儲,這些狀態(tài)數(shù)據(jù)在應(yīng)用級別都是可訪問的耕渴。AppStorage將在應(yīng)用運(yùn)行過程保留其屬性拘悦。屬性通過唯一的鍵字符串值訪問。
AppStorage可以和UI組件同步橱脸,且可以在應(yīng)用業(yè)務(wù)邏輯中被訪問础米。
AppStorage中的屬性可以被雙向同步,數(shù)據(jù)可以是存在于本地或遠(yuǎn)程設(shè)備上添诉,并具有不同的功能屁桑,比如數(shù)據(jù)持久化(詳見PersistentStorage)忱反。這些數(shù)據(jù)是通過業(yè)務(wù)邏輯中實(shí)現(xiàn)炕横,與UI解耦,如果希望這些數(shù)據(jù)在UI中使用边翼,需要用到@StorageProp和@StorageLink须眷。
PersistentStorage:持久化存儲UI狀態(tài)
前兩個小節(jié)介紹的LocalStorage和AppStorage都是運(yùn)行時的內(nèi)存竖瘾,但是在應(yīng)用退出再次啟動后,依然能保存選定的結(jié)果花颗,是應(yīng)用開發(fā)中十分常見的現(xiàn)象捕传,這就需要用到PersistentStorage。
PersistentStorage是應(yīng)用程序中的可選單例對象扩劝。此對象的作用是持久化存儲選定的AppStorage屬性庸论,以確保這些屬性在應(yīng)用程序重新啟動時的值與應(yīng)用程序關(guān)閉時的值相同。
PersistentStorage將選定的AppStorage屬性保留在設(shè)備磁盤上棒呛。應(yīng)用程序通過API聂示,以決定哪些AppStorage屬性應(yīng)借助PersistentStorage持久化。UI和業(yè)務(wù)邏輯不直接訪問PersistentStorage中的屬性簇秒,所有屬性訪問都是對AppStorage的訪問鱼喉,AppStorage中的更改會自動同步到PersistentStorage。
PersistentStorage和AppStorage中的屬性建立雙向同步宰睡。應(yīng)用開發(fā)通常通過AppStorage訪問PersistentStorage,另外還有一些接口可以用于管理持久化屬性气筋,但是業(yè)務(wù)邏輯始終是通過AppStorage獲取和設(shè)置屬性的拆内。
限制條件
PersistentStorage允許的類型和值有:
- number, string, boolean, enum 等簡單類型。
- 可以被JSON.stringify()和JSON.parse()重構(gòu)的對象宠默。例如Date, Map, Set等內(nèi)置類型則不支持麸恍,以及對象的屬性方法不支持持久化。
PersistentStorage不允許的類型和值有:
- 不支持嵌套對象(對象數(shù)組,對象的屬性是對象等)抹沪。因?yàn)槟壳翱蚣軣o法檢測AppStorage中嵌套對象(包括數(shù)組)值的變化刻肄,所以無法寫回到PersistentStorage中。
- 不支持undefined 和 null 融欧。
持久化數(shù)據(jù)是一個相對緩慢的操作敏弃,應(yīng)用程序應(yīng)避免以下情況:
- 持久化大型數(shù)據(jù)集。
- 持久化經(jīng)常變化的變量噪馏。
PersistentStorage的持久化變量最好是小于2kb的數(shù)據(jù)麦到,不要大量的數(shù)據(jù)持久化,因?yàn)镻ersistentStorage寫入磁盤的操作是同步的欠肾,大量的數(shù)據(jù)本地化讀寫會同步在UI線程中執(zhí)行瓶颠,影響UI渲染性能。如果開發(fā)者需要存儲大量的數(shù)據(jù)刺桃,建議使用數(shù)據(jù)庫api粹淋。
PersistentStorage只能在UI頁面內(nèi)使用,否則將無法持久化數(shù)據(jù)瑟慈。
Environment:設(shè)備環(huán)境查詢
開發(fā)者如果需要應(yīng)用程序運(yùn)行的設(shè)備的環(huán)境參數(shù)桃移,以此來作出不同的場景判斷,比如多語言封豪,暗黑模式等谴轮,需要用到Environment設(shè)備環(huán)境查詢。
Environment是ArkUI框架在應(yīng)用程序啟動時創(chuàng)建的單例對象吹埠。它為AppStorage提供了一系列描述應(yīng)用程序運(yùn)行狀態(tài)的屬性第步。Environment的所有屬性都是不可變的(即應(yīng)用不可寫入),所有的屬性都是簡單類型缘琅。
// 將設(shè)備languageCode存入AppStorage中
Environment.EnvProp('languageCode', 'en');
@Entry
@Component
struct Index {
@StorageProp('languageCode') languageCode: string = 'en';
build() {
Row() {
Column() {
// 輸出當(dāng)前設(shè)備的languageCode
Text(this.languageCode)
}
}
}
}
應(yīng)用邏輯使用Environment
// 使用Environment.EnvProp將設(shè)備運(yùn)行l(wèi)anguageCode存入AppStorage中粘都;
Environment.EnvProp('languageCode', 'en');
// 從AppStorage獲取單向綁定的languageCode的變量
const lang: SubscribedAbstractProperty<string> = AppStorage.Prop('languageCode');
if (lang.get() === 'zh') {
console.info('你好');
} else {
console.info('Hello!');
}