LocalStorage:页面级UI状态存储
developer.huawei.com/consumer/cn…
LocalStorage是页面级的UI状态存储,通过@Entry装饰器接收的参数可以在页面内共享同一个LocalStorage实例。LocalStorage支持UIAbility实例内多个页面间状态共享
@LocalStorageProp
@LocalStorageProp装饰的变量与LocalStorage中给定属性建立单向同步关系
@LocalStorageProp初始化规则图示
框架行为
- 被@LocalStorageProp装饰的变量的值的变化不会同步回LocalStorage里。
- @LocalStorageProp装饰的变量变化会使当前自定义组件中关联的组件刷新。
- LocalStorage(key)中值的变化会引发所有被@LocalStorageProp对应key装饰的变量的变化,会覆盖@LocalStorageProp本地的改变。
@LocalStorageLink
@LocalStorageLink装饰的变量与LocalStorage中给定属性建立双向同步关系
@LocalStorageLink初始化规则图示
框架行为
- 当@LocalStorageLink(key)装饰的数值改变被观察到时,修改将被同步回LocalStorage对应属性键值key的属性中。
- LocalStorage中属性键值key对应的数据一旦改变,属性键值key绑定的所有的数据(包括双向@LocalStorageLink和单向@LocalStorageProp)都将同步修改。
- 当@LocalStorageLink(key)装饰的数据本身是状态变量,它的改变不仅仅会同步回LocalStorage中,还会引起所属的自定义组件的重新渲染。
转换前代码
js 代码解读复制代码/**
*
* LocalStorageCmpt.ets
* Created by unravel on 2024/5/3
* @abstract
*/
import { StorageDemoClass } from './StateDemoPage'
@Component
export struct LocalStorageCmpt {
// localstorage
@LocalStorageProp('localStorageSimpleValueKey')
localStoragePropSimpleValueVar: string = 'localStoragePropSimpleValueValue'
@LocalStorageLink('localStorageObjectKey')
localStorageLinkObjectVar: StorageDemoClass = new StorageDemoClass()
aboutToAppear(): void {
}
build() {
Column() {
Text('localStoragePropSimpleValue: ' + this.localStoragePropSimpleValueVar)
Text('localStorageLinkObject: ' + this.localStorageLinkObjectVar.name)
}.onClick(() => {
this.localStoragePropSimpleValueVar = 'cmpLocalStorageSimpleValue1'
this.localStorageLinkObjectVar.name = 'cmpLocalStorageObject.name'
})
}
}
转换后代码
js 代码解读复制代码if (!("finalizeConstruction" in ViewPU.prototype)) {
Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { });
}
interface LocalStorageCmpt_Params {
localStoragePropSimpleValueVar?: string;
localStorageLinkObjectVar?: StorageDemoClass;
}
import { StorageDemoClass } from "@bundle:com.unravel.myapplication/entry/ets/pages/StateDemoPage";
export class LocalStorageCmpt extends ViewPU {
constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) {
super(parent, __localStorage, elmtId, extraInfo);
if (typeof paramsLambda === "function") {
this.paramsGenerator_ = paramsLambda;
}
this.setInitiallyProvidedValue(params);
this.finalizeConstruction();
}
setInitiallyProvidedValue(params: LocalStorageCmpt_Params) {
}
updateStateVars(params: LocalStorageCmpt_Params) {
}
purgeVariableDependenciesOnElmtId(rmElmtId) {
this.__localStoragePropSimpleValueVar.purgeDependencyOnElmtId(rmElmtId);
this.__localStorageLinkObjectVar.purgeDependencyOnElmtId(rmElmtId);
}
aboutToBeDeleted() {
this.__localStoragePropSimpleValueVar.aboutToBeDeleted();
this.__localStorageLinkObjectVar.aboutToBeDeleted();
SubscriberManager.Get().delete(this.id__());
this.aboutToBeDeletedInternal();
}
// localstorage
private __localStoragePropSimpleValueVar: ObservedPropertyAbstractPU =
this.createLocalStorageProp('localStorageSimpleValueKey',
'localStoragePropSimpleValueValue',
"localStoragePropSimpleValueVar");
get localStoragePropSimpleValueVar() {
return this.__localStoragePropSimpleValueVar.get();
}
set localStoragePropSimpleValueVar(newValue: string) {
this.__localStoragePropSimpleValueVar.set(newValue);
}
private __localStorageLinkObjectVar: ObservedPropertyAbstractPU<StorageDemoClass> =
this.createLocalStorageLink<StorageDemoClass>('localStorageObjectKey',
new StorageDemoClass(),
"localStorageLinkObjectVar");
get localStorageLinkObjectVar() {
return this.__localStorageLinkObjectVar.get();
}
set localStorageLinkObjectVar(newValue: StorageDemoClass) {
this.__localStorageLinkObjectVar.set(newValue);
}
aboutToAppear(): void {
}
initialRender() {
this.observeComponentCreation2((elmtId, isInitialRender) => {
Column.create();
Column.onClick(() => {
this.localStoragePropSimpleValueVar = 'cmpLocalStorageSimpleValue1';
this.localStorageLinkObjectVar.name = 'cmpLocalStorageObject.name';
});
}, Column);
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create('localStoragePropSimpleValue: ' + this.localStoragePropSimpleValueVar);
}, Text);
Text.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create('localStorageLinkObject: ' + this.localStorageLinkObjectVar.name);
}, Text);
Text.pop();
Column.pop();
}
rerender() {
this.updateDirtyElements();
}
}
传递
这里我们的写法是显示的将storage传进了LocalStorageCmpt这个组件。
如果我们不显示传递,默认会传入一个undefined的storage
@LocalStorageProp
转换前后
转换成TS之后,和@State的操作类似。
- 将原有属性重写为getter和setter
- 声明一个以双下划线开头的类型为ObservedPropertyAbstractPU的私有属性,并通过this.createLocalStorageProp创建该属性值
- ObservedPropertyAbstractPU这个属性我们很熟悉了,它就是@State状态变量的数据源
ViewPU
public createLocalStorageProp(storagePropName: string, defaultValue: T, viewVariableName: string): ObservedPropertyAbstractPU
这个函数我们在鸿蒙ACE-ArkUI构建(二)、渲染控制和构建过程 提到过,这里再看下具体实现
我们看到这里是通过 this.localStorage_.__createSync 创建的
localStorage_是getter、setter
我们看下localStorage_从哪里来的。可以看到localStorage_实际上是getter和setter,真正存储的是 localStoragebackStore_
localStoragebackStore_ 实际存储storage
组件初始化的时候如果传递了storage,则将传递过来的storage赋值给 localStoragebackStore_
public get localStorage_(): LocalStorage
localStorage_会递归往父组件找,如果没找到会创建一个默认的storage。
这一块的逻辑和Provide很相似,可以回忆一下Provide是怎么查找对应key的状态变量的
LocalStorage
public __createSync(storagePropName: string, defaultValue: T, factoryFunc: SynchedPropertyFactoryFunc): ObservedPropertyAbstract
先通过storage_根据属性名称取值,没有值的话会通过addNewPropertyInternal创建一个新的属性p,然后将p作为参数调用factoryFunc
storage_ 是一个Map
这个storage_ 实际上是一个map,用于按属性名存储ObservedPropertyAbstract实例
private addNewPropertyInternal(propName: string, value: T): ObservedPropertyAbstract
这个函数实际创建ObservedPropertyAbstract实例并存储到map中,最终将创建的ObservedPropertyAbstract实例返回
factoryFunc
factoryFun就是我们传入的闭包函数,它的最终实现其实是创建了一个SynchedPropertyObjectOneWay的单向同步实例
小结
- 每个组件都是一个ViewPU,在ViewPU内部有一个localStorageStore_ 属性,用于存储localStorage实例。
- localStorage实例实际上是getter和setter,并不实际存储storage的值。实际存储storage的类是 localStoragebackStore_
- 如果从父组件传递了storage过来,就是用传递过来的storage,否则会从父组件开始递归查找,如果最终没有找到storage,则创建一个默认的storage
- @LocalStorageProp转换后是一个类型为SynchedPropertyObjectOneWay的单向同步实例
- 可以看到@LocalStorageProp和@Prop很类似,除了多了一个查找storage中对应key的状态变量的过程
get、set
可以看到@LocalStorageProp装饰的变量的setter和getter实际上是操作的ObservedPropertyAbstract的get和set方法
ObservedPropertyAbstract是@State状态包装类的父类,所以这里的get和set也和@State的一样(关联UI、UI刷新等) 鸿蒙ACE-V1状态分析@Prop
storage.setOrCreate('key',value) 怎么驱动UI更新?
LocalStorage
public setOrCreate(propName: string, newValue: T): boolean
上面我们看的是@LocalStorageProp状态变量关联和驱动UI的逻辑。那么storage.setOrCreate('key',value)方法设置key的值之后,UI为什么会更新呢?
setOrCreate先取出key键关联的对象,然后调用了对象的set方法,后面就和@State状态变量更新导致@Prop更新一样了,可以参考 鸿蒙ACE-V1状态分析@Prop
storage.prop('key').set(222)又是怎么驱动UI更新?
LocalStorage
public prop(propName: string, propUser?: IPropertySubscriber, subscribersName?: string): SubscribedAbstractProperty | undefined
storage.prop('key') 和 @LocalStorageProp 实现几乎一样,都是返回的一个SynchedPropertyOneWayPU,这个对象会跟随ObservedPropertyPU的set方法一起更新
@LocalStorageLink
转换前后
转换成TS之后,和@State的操作类似。
- 将原有属性重写为getter和setter
- 声明一个以双下划线开头的类型为ObservedPropertyAbstractPU的私有属性,并通过this.createLocalLink创建该属性值
ViewPU
public createLocalStorageLink(storagePropName: string, defaultValue: T, viewVariableName: string): ObservedPropertyAbstractPU
这个过程和createLocalStorageProp的过程非常相似,唯一的区别是factoryFunc的实现
createLocalStorageProp是创建一个 SynchedPropertyObjectOneWayPU。看到这个类你应该想起了@Prop
而createLocalStorageLink是创建一个 SynchedPropertyTwoWayPU。看到这个类你应该想起了@Link
一旦找到对应key的状态类实例,就和@Link的操作是一样的了。因为所有的API都是先通过key拿到key对应的状态实例,然后进行操作
小结
- @LocalStorageLink的创建过程和@LocalStorageProp的创建过程非常类似,只是factoryFun不一样
- @LocalStorageProp和@LocalStorageLink与@Prop和@Link几乎一样,只不多过了一个查找对应key状态变量的过程。而这个key存储的就是类是和@Prop或@Link底层包装类是一样的
总结
- storage里面有一个Map用于存储键值对应的状态变量,可以理解为一个key对应一个@State,而@LocalStorageProp则相当于@Prop,@LocalStorageLink相当于@Link
- 可以把storage理解每个key对应一个@State的map,是一个集合
评论记录:
回复评论: