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)屬性提供中央存儲。
和LocalStorage不同的是,LocalStorage是頁面級的,通常應(yīng)用于頁面內(nèi)的數(shù)據(jù)共享。而對于AppStorage,是應(yīng)用級的全局狀態(tài)共享。
概述
AppStorage是在應(yīng)用啟動的時候會被創(chuàng)建的單例。它的目的是為了提供應(yīng)用狀態(tài)數(shù)據(jù)的中心存儲,這些狀態(tài)數(shù)據(jù)在應(yīng)用級別都是可訪問的。AppStorage將在應(yīng)用運行過程保留其屬性。屬性通過唯一的鍵字符串值訪問。
AppStorage可以和UI組件同步,且可以在應(yīng)用業(yè)務(wù)邏輯中被訪問。
AppStorage中的屬性可以被雙向同步,數(shù)據(jù)可以是存在于本地或遠(yuǎn)程設(shè)備上,并具有不同的功能,比如數(shù)據(jù)持久化。
@StorageProp
在上文中已經(jīng)提到,如果要建立AppStorage和自定義組件的聯(lián)系,需要使用@StorageProp和@StorageLink裝飾器。使用@StorageProp(key)/@StorageLink(key)裝飾組件內(nèi)的變量,key標(biāo)識了AppStorage的屬性。
當(dāng)自定義組件初始化的時候,@StorageProp(key)/@StorageLink(key)裝飾的變量會通過給定的key,綁定在AppStorage對應(yīng)的屬性,完成初始化。本地初始化是必要的,因為無法保證AppStorage一定存在給定的key,這取決于應(yīng)用邏輯,是否在組件初始化之前在AppStorage實例中存入對應(yīng)的屬性。
@StorageProp(key)是和AppStorage中key對應(yīng)的屬性建立單向數(shù)據(jù)同步,我們允許本地改變的發(fā)生,但是對于@StorageProp,本地的修改永遠(yuǎn)不會同步回AppStorage中,相反,如果AppStorage給定key的屬性發(fā)生改變,改變會被同步給@StorageProp,并覆蓋掉本地的修改。
裝飾器使用規(guī)則說明
@StorageProp變量裝飾器 |
說明 |
---|---|
裝飾器參數(shù) |
key:常量字符串,必填(字符串需要有引號)。 |
允許裝飾的變量類型 |
Object class、string、number、boolean、enum類型,以及這些類型的數(shù)組。 類型必須被指定,且必須和LocalStorage中對應(yīng)屬性相同。不支持any,不允許使用undefined和null。 |
同步類型 |
單向同步:從AppStorage的對應(yīng)屬性到組件的狀態(tài)變量。 組件本地的修改是允許的,但是AppStorage中給定的屬性一旦發(fā)生變化,將覆蓋本地的修改。 |
被裝飾變量的初始值 |
必須指定,如果AppStorage實例中不存在屬性,則作為初始化默認(rèn)值,并存入AppStorage中。 |
變量的傳遞/訪問規(guī)則說明
傳遞/訪問 |
說明 |
---|---|
從父節(jié)點初始化和更新 |
禁止,@StorageProp不支持從父節(jié)點初始化,只能AppStorage中key對應(yīng)的屬性初始化,如果沒有對應(yīng)key的話,將使用本地默認(rèn)值初始化 |
初始化子節(jié)點 |
支持,可用于初始化@State、@Link、@Prop、@Provide。 |
是否支持組件外訪問 |
否。 |
圖1?@StorageProp初始化規(guī)則圖示
觀察變化和行為表現(xiàn)
觀察變化
- 當(dāng)裝飾的數(shù)據(jù)類型為boolean、string、number類型時,可以觀察到數(shù)值的變化。
- 當(dāng)裝飾的數(shù)據(jù)類型為class或者Object時,可以觀察到賦值和屬性賦值的變化,即Object.keys(observedObject)返回的所有屬性。
- 當(dāng)裝飾的對象是array時,可以觀察到數(shù)組添加、刪除、更新數(shù)組單元的變化。
框架行為
- 當(dāng)@StorageProp(key)裝飾的數(shù)值改變被觀察到時,修改不會被同步回AppStorage對應(yīng)屬性鍵值key的屬性中。
- 當(dāng)前@StorageProp(key)單向綁定的數(shù)據(jù)會被修改,即僅限于當(dāng)前組件的私有成員變量改變,其他的綁定該key的數(shù)據(jù)不會同步改變。
- 當(dāng)@StorageProp(key)裝飾的數(shù)據(jù)本身是狀態(tài)變量,它的改變雖然不會同步回AppStorage中,但是會引起所屬的自定義組件的重新渲染。
- 當(dāng)AppStorage中key對應(yīng)的屬性發(fā)生改變時,會同步給所有@StorageProp(key)裝飾的數(shù)據(jù),@StorageProp(key)本地的修改將被覆蓋。
@StorageLink
@StorageLink(key)是和AppStorage中key對應(yīng)的屬性建立雙向數(shù)據(jù)同步:
- 本地修改發(fā)生,該修改會被寫回AppStorage中;
- AppStorage中的修改發(fā)生后,該修改會被同步到所有綁定AppStorage對應(yīng)key的屬性上,包括單向(@StorageProp和通過Prop創(chuàng)建的單向綁定變量)、雙向(@StorageLink和通過Link創(chuàng)建的雙向綁定變量)變量和其他實例(比如PersistentStorage)。
裝飾器使用規(guī)則說明
@StorageLink變量裝飾器 |
說明 |
---|---|
裝飾器參數(shù) |
key:常量字符串,必填(字符串需要有引號)。 |
允許裝飾的變量類型 |
Object、class、string、number、boolean、enum類型,以及這些類型的數(shù)組。 類型必須被指定,且必須和AppStorage中對應(yīng)屬性相同。不支持any,不允許使用undefined和null。 |
同步類型 |
雙向同步:從AppStorage的對應(yīng)屬性到自定義組件,從自定義組件到AppStorage對應(yīng)屬性。 |
被裝飾變量的初始值 |
必須指定,如果AppStorage實例中不存在屬性,則作為初始化默認(rèn)值,并存入AppStorage中。 |
變量的傳遞/訪問規(guī)則說明
傳遞/訪問 |
說明 |
---|---|
從父節(jié)點初始化和更新 |
禁止。 |
初始化子節(jié)點 |
支持,可用于初始化常規(guī)變量、@State、@Link、@Prop、@Provide。 |
是否支持組件外訪問 |
否。 |
圖2?@StorageLink初始化規(guī)則圖示
觀察變化和行為表現(xiàn)
觀察變化
- 當(dāng)裝飾的數(shù)據(jù)類型為boolean、string、number類型時,可以觀察到數(shù)值的變化。
- 當(dāng)裝飾的數(shù)據(jù)類型為class或者Object時,可以觀察到賦值和屬性賦值的變化,即Object.keys(observedObject)返回的所有屬性。
- 當(dāng)裝飾的對象是array時,可以觀察到數(shù)組添加、刪除、更新數(shù)組單元的變化。
框架行為
- 當(dāng)@StorageLink(key)裝飾的數(shù)值改變被觀察到時,修改將被同步回AppStorage對應(yīng)屬性鍵值key的屬性中。
- AppStorage中屬性鍵值key對應(yīng)的數(shù)據(jù)一旦改變,屬性鍵值key綁定的所有的數(shù)據(jù)(包括雙向@StorageLink和單向@StorageProp)都將同步修改;
- 當(dāng)@StorageLink(key)裝飾的數(shù)據(jù)本身是狀態(tài)變量,它的改變不僅僅會同步回AppStorage中,還會引起所屬的自定義組件的重新渲染。
使用場景
從應(yīng)用邏輯使用AppStorage和LocalStorage
AppStorage是單例,它的所有API都是靜態(tài)的,使用方法類似于LocalStorage對應(yīng)的非靜態(tài)方法。
AppStorage.SetOrCreate('PropA', 47);
let storage: LocalStorage = new LocalStorage({ 'PropA': 17 });
let propA: number = AppStorage.Get('PropA') // propA in AppStorage == 47, propA in LocalStorage == 17
var link1: SubscribedAbstractProperty<number> = AppStorage.Link('PropA'); // link1.get() == 47
var link2: SubscribedAbstractProperty<number> = AppStorage.Link('PropA'); // link2.get() == 47
var prop: SubscribedAbstractProperty<number> = AppStorage.Prop('PropA'); // prop.get() = 47
link1.set(48); // two-way sync: link1.get() == link2.get() == prop.get() == 48
prop.set(1); // one-way sync: prop.get()=1; but link1.get() == link2.get() == 48
link1.set(49); // two-way sync: link1.get() == link2.get() == prop.get() == 49
storage.get('PropA') // == 17
storage.set('PropA', 101);
storage.get('PropA') // == 101
AppStorage.Get('PropA') // == 49
link1.get() // == 49
link2.get() // == 49
prop.get() // == 49
從UI內(nèi)部使用AppStorage和LocalStorage
@StorageLink變量裝飾器與AppStorage配合使用,正如@LocalStorageLink與LocalStorage配合使用一樣。此裝飾器使用AppStorage中的屬性創(chuàng)建雙向數(shù)據(jù)同步。
AppStorage.SetOrCreate('PropA', 47);
let storage = new LocalStorage({ 'PropA': 48 });
@Entry(storage)
@Component
struct CompA {
@StorageLink('PropA') storLink: number = 1;
@LocalStorageLink('PropA') localStorLink: number = 1;
build() {
Column({ space: 20 }) {
Text(`From AppStorage ${this.storLink}`)
.onClick(() => this.storLink += 1)
Text(`From LocalStorage ${this.localStorLink}`)
.onClick(() => this.localStorLink += 1)
}
}
}
以持久化方式訂閱某個事件并接收事件回調(diào)
推薦使用持久化方式訂閱某個事件并接收事件回調(diào),可以減少開銷,增強代碼的可讀性。
// xxx.ets
import emitter from '@ohos.events.emitter';
let NextID: number = 0;
class ViewData {
title: string;
uri: Resource;
color: Color = Color.Black;
id: number;
constructor(title: string, uri: Resource) {
this.title = title;
this.uri = uri
this.id = NextID++;
}
}
@Entry
@Component
struct Gallery2 {
dataList: Array<ViewData> = [new ViewData('flower', $r('app.media.icon')), new ViewData('OMG', $r('app.media.icon')), new ViewData('OMG', $r('app.media.icon'))]
scroller: Scroller = new Scroller()
private preIndex: number = -1
build() {
Column() {
Grid(this.scroller) {
ForEach(this.dataList, (item: ViewData) => {
GridItem() {
TapImage({
uri: item.uri,
index: item.id
})
}.aspectRatio(1)
.onClick(() => {
if (this.preIndex === item.id) {
return
}
var innerEvent = { eventId: item.id }
// 選中態(tài):黑變紅
var eventData = {
data: {
"colorTag": 1
}
}
emitter.emit(innerEvent, eventData)
if (this.preIndex != -1) {
console.info(`preIndex: ${this.preIndex}, index: ${item.id}, black`)
var innerEvent = { eventId: this.preIndex }
// 取消選中態(tài):紅變黑
var eventData = {
data: {
"colorTag": 0
}
}
emitter.emit(innerEvent, eventData)
}
this.preIndex = item.id
})
}, (item: ViewData) => JSON.stringify(item))
}.columnsTemplate('1fr 1fr')
}
}
}
@Component
export struct TapImage {
@State tapColor: Color = Color.Black;
private index: number;
private uri: Resource;
onTapIndexChange(colorTag: emitter.EventData) {
this.tapColor = colorTag.data.colorTag ? Color.Red : Color.Black
}
aboutToAppear() {
//定義事件ID
var innerEvent = { eventId: this.index }
emitter.on(innerEvent, this.onTapIndexChange.bind(this))
}
build() {
Column() {
Image(this.uri)
.objectFit(ImageFit.Cover)
.border({ width: 5, style: BorderStyle.Dotted, color: this.tapColor })
}
}
}
以下示例為消息機制方式訂閱事件,會導(dǎo)致回調(diào)監(jiān)聽的節(jié)點數(shù)較多,非常耗時,不推薦以此來實現(xiàn)應(yīng)用代碼。文章來源:http://www.zghlxwxcb.cn/news/detail-829705.html
// xxx.ets
class ViewData {
title: string;
uri: Resource;
color: Color = Color.Black;
constructor(title: string, uri: Resource) {
this.title = title;
this.uri = uri
}
}
@Entry
@Component
struct Gallery2 {
dataList: Array<ViewData> = [new ViewData('flower', $r('app.media.icon')), new ViewData('OMG', $r('app.media.icon')), new ViewData('OMG', $r('app.media.icon'))]
scroller: Scroller = new Scroller()
build() {
Column() {
Grid(this.scroller) {
ForEach(this.dataList, (item: ViewData, index?: number) => {
GridItem() {
TapImage({
uri: item.uri,
index: index
})
}.aspectRatio(1)
}, (item: ViewData, index?: number) => {
return JSON.stringify(item) + index;
})
}.columnsTemplate('1fr 1fr')
}
}
}
@Component
export struct TapImage {
@StorageLink('tapIndex') @Watch('onTapIndexChange') tapIndex: number = -1;
@State tapColor: Color = Color.Black;
private index: number;
private uri: Resource;
// 判斷是否被選中
onTapIndexChange() {
if (this.tapIndex >= 0 && this.index === this.tapIndex) {
console.info(`tapindex: ${this.tapIndex}, index: ${this.index}, red`)
this.tapColor = Color.Red;
} else {
console.info(`tapindex: ${this.tapIndex}, index: ${this.index}, black`)
this.tapColor = Color.Black;
}
}
build() {
Column() {
Image(this.uri)
.objectFit(ImageFit.Cover)
.onClick(() => {
this.tapIndex = this.index;
})
.border({ width: 5, style: BorderStyle.Dotted, color: this.tapColor })
}
}
}
限制條件
AppStorage與PersistentStorage以及Environment配合使用時,需要注意以下幾點:文章來源地址http://www.zghlxwxcb.cn/news/detail-829705.html
- 在AppStorage中創(chuàng)建屬性后,調(diào)用PersistentStorage.PersistProp()接口時,會使用在AppStorage中已經(jīng)存在的值,并覆蓋PersistentStorage中的同名屬性,所以建議要使用相反的調(diào)用順序;
- 如果在AppStorage中已經(jīng)創(chuàng)建屬性后,再調(diào)用Environment.EnvProp()創(chuàng)建同名的屬性,會調(diào)用失敗。因為AppStorage已經(jīng)有同名屬性,Environment環(huán)境變量不會再寫入AppStorage中,所以建議AppStorage中屬性不要使用Environment預(yù)置環(huán)境變量名。
到了這里,關(guān)于HarmonyOS學(xué)習(xí)路之方舟開發(fā)框架—學(xué)習(xí)ArkTS語言(狀態(tài)管理 六)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!