W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗(yàn)值獎勵
AppStorage是應(yīng)用全局的UI狀態(tài)存儲,是和應(yīng)用的進(jìn)程綁定的,由UI框架在應(yīng)用程序啟動時(shí)創(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)用啟動的時(shí)候會被創(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。
在上文中已經(jīng)提到,如果要建立AppStorage和自定義組件的聯(lián)系,需要使用@StorageProp和@StorageLink裝飾器。使用@StorageProp(key)/@StorageLink(key)裝飾組件內(nèi)的變量,key標(biāo)識了AppStorage的屬性。
當(dāng)自定義組件初始化的時(shí)候,@StorageProp(key)/@StorageLink(key)裝飾的變量會通過給定的key,綁定在AppStorage對應(yīng)的屬性,完成初始化。本地初始化是必要的,因?yàn)闊o法保證AppStorage一定存在給定的key,這取決于應(yīng)用邏輯,是否在組件初始化之前在AppStorage實(shí)例中存入對應(yīng)的屬性。
@StorageProp(key)是和AppStorage中key對應(yīng)的屬性建立單向數(shù)據(jù)同步,我們允許本地改變的發(fā)生,但是對于@StorageProp,本地的修改永遠(yuǎn)不會同步回AppStorage中,相反,如果AppStorage給定key的屬性發(fā)生改變,改變會被同步給@StorageProp,并覆蓋掉本地的修改。
@StorageProp變量裝飾器 | 說明 |
---|---|
裝飾器參數(shù) | key:常量字符串,必填(字符串需要有引號)。 |
允許裝飾的變量類型 | Object class、string、number、boolean、enum類型,以及這些類型的數(shù)組。嵌套類型的場景請參考觀察變化和行為表現(xiàn)。 類型必須被指定,且必須和AppStorage中對應(yīng)屬性相同。不支持any,不允許使用undefined和null。 |
同步類型 | 單向同步:從AppStorage的對應(yīng)屬性到組件的狀態(tài)變量。 組件本地的修改是允許的,但是AppStorage中給定的屬性一旦發(fā)生變化,將覆蓋本地的修改。 |
被裝飾變量的初始值 | 必須指定,如果AppStorage實(shí)例中不存在屬性,則作為初始化默認(rèn)值,并存入AppStorage中。 |
傳遞/訪問 | 說明 |
---|---|
從父節(jié)點(diǎn)初始化和更新 | 禁止,@StorageProp不支持從父節(jié)點(diǎn)初始化,只能AppStorage中key對應(yīng)的屬性初始化,如果沒有對應(yīng)key的話,將使用本地默認(rèn)值初始化 |
初始化子節(jié)點(diǎn) | 支持,可用于初始化@State、@Link、@Prop、@Provide。 |
是否支持組件外訪問 | 否。 |
框架行為
@StorageLink(key)是和AppStorage中key對應(yīng)的屬性建立雙向數(shù)據(jù)同步:
@StorageLink變量裝飾器 | 說明 |
---|---|
裝飾器參數(shù) | key:常量字符串,必填(字符串需要有引號)。 |
允許裝飾的變量類型 | Object、class、string、number、boolean、enum類型,以及這些類型的數(shù)組。嵌套類型的場景請參考觀察變化和行為表現(xiàn)。 類型必須被指定,且必須和AppStorage中對應(yīng)屬性相同。不支持any,不允許使用undefined和null。 |
同步類型 | 雙向同步:從AppStorage的對應(yīng)屬性到自定義組件,從自定義組件到AppStorage對應(yīng)屬性。 |
被裝飾變量的初始值 | 必須指定,如果AppStorage實(shí)例中不存在屬性,則作為初始化默認(rèn)值,并存入AppStorage中。 |
傳遞/訪問 | 說明 |
---|---|
從父節(jié)點(diǎn)初始化和更新 | 禁止。 |
初始化子節(jié)點(diǎn) | 支持,可用于初始化常規(guī)變量、@State、@Link、@Prop、@Provide。 |
是否支持組件外訪問 | 否。 |
框架行為
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
@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)
- }
- }
- }
不建議開發(fā)者使用@StorageLink和AppStorage的雙向同步的機(jī)制來實(shí)現(xiàn)事件通知,AppStorage是和UI相關(guān)的數(shù)據(jù)存儲,改變會帶來UI的刷新,相對于一般的事件通知,UI刷新的成本較大。
TapImage中的點(diǎn)擊事件,會觸發(fā)AppStorage中tapIndex對應(yīng)屬性的改變。因?yàn)锧StorageLink是雙向同步,修改會同步回AppStorage中,所以,所有綁定AppStorage的tapIndex自定義組件都會被通知UI刷新。UI刷新帶來的成本是巨大的,因此不建議開發(fā)者使用此方式來實(shí)現(xiàn)基本的事件通知功能。
- // 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 = 0;
- private uri: Resource = {
- id: 0,
- type: 0,
- moduleName: "",
- bundleName: ""
- };
- // 判斷是否被選中
- 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 })
- }
- }
- }
開發(fā)者可以使用emit訂閱某個事件并接收事件回調(diào),可以減少開銷,增強(qiáng)代碼的可讀性。
- // 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
- }
- let innerEvent: emitter.InnerEvent = { eventId: item.id }
- // 選中態(tài):黑變紅
- let eventData: emitter.EventData = {
- data: {
- "colorTag": 1
- }
- }
- emitter.emit(innerEvent, eventData)
- if (this.preIndex != -1) {
- console.info(`preIndex: ${this.preIndex}, index: ${item.id}, black`)
- let innerEvent: emitter.InnerEvent = { eventId: this.preIndex }
- // 取消選中態(tài):紅變黑
- let eventData: emitter.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 = 0;
- private uri: Resource = {
- id: 0,
- type: 0,
- moduleName: "",
- bundleName: ""
- };
- onTapIndexChange(colorTag: emitter.EventData) {
- if (colorTag.data != null) {
- this.tapColor = colorTag.data.colorTag ? Color.Red : Color.Black
- }
- }
- aboutToAppear() {
- //定義事件ID
- let innerEvent: emitter.InnerEvent = { eventId: this.index }
- emitter.on(innerEvent, data => {
- this.onTapIndexChange(data)
- })
- }
- build() {
- Column() {
- Image(this.uri)
- .objectFit(ImageFit.Cover)
- .border({ width: 5, style: BorderStyle.Dotted, color: this.tapColor })
- }
- }
- }
以上通知事件邏輯簡單,也可以簡化成三元表達(dá)式。
- // 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') tapIndex: number = -1;
- @State tapColor: Color = Color.Black;
- private index: number = 0;
- private uri: Resource = {
- id: 0,
- type: 0,
- moduleName: "",
- bundleName: ""
- };
- build() {
- Column() {
- Image(this.uri)
- .objectFit(ImageFit.Cover)
- .onClick(() => {
- this.tapIndex = this.index;
- })
- .border({
- width: 5,
- style: BorderStyle.Dotted,
- color: (this.tapIndex >= 0 && this.index === this.tapIndex) ? Color.Red : Color.Black
- })
- }
- }
- }
AppStorage與PersistentStorage以及Environment配合使用時(shí),需要注意以下幾點(diǎn):
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: