@State装饰器:组件内状态
developer.huawei.com/consumer/cn…
初始化规则如下图
@State 可以观察基本类型、class、Map、Set等的自身的赋值的变化,和其属性赋值的变化,即Object.keys(observedObject)返回的所有属性
框架行为
- 当状态变量被改变时,查询依赖该状态变量的组件;
- 执行依赖该状态变量的组件的更新方法,组件更新渲染;
- 和该状态变量不相关的组件或者UI描述不会发生重新渲染,从而实现页面渲染的按需更新。
转换前代码
import { PropChildCmpt } from './PropChildCmpt';
import { LinkChildCmpt } from './LinkChildCmpt';
import { ClassA, ClassB, ObjectLinkCmpt } from './ObjectLinkCmpt';
import { LocalStorageCmpt } from './LocalStorageCmpt';
import { AppStorageCmpt } from './AppStorageCmpt';
import { WatchChildCmpt } from './WatchChildCmpt';
import { TwoWayCmpt } from './TwoWayCmpt';
export class StateClass {
public value: string;
constructor(value: string) {
this.value = value;
}
}
export interface StateInterface {
value: string;
}
export class StorageDemoClass {
name: string
age: number
constructor(
name: string = '',
age: number = 10
) {
this.name = name
this.age = age
}
}
// LocalStorage可以观察到基本类型、class、Object、Map、Set、Date等的属性变化
let storage: LocalStorage = new LocalStorage();
storage.setOrCreate('localStorageSimpleValue', 47)
storage.setOrCreate('localStorageObject', new StorageDemoClass())
@Entry({ storage })
@Component
struct StateDemoPage {
// 简单类型
@State simpleState: number = 0;
// class类型
@State classState: StateClass = new StateClass('Hello');
// interface类型
@State interfaceState: StateInterface = { value: 'World' };
// 数组类型
@State arrayState: StateClass[] = [new StateClass('a'), new StateClass('b')];
// 日期类型
@State dateState: Date = new Date('2021-08-08')
// map类型
@State mapState: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]])
// Set类型
@State setState: Set<number> = new Set([0, 1, 2, 3, 4])
// 联合类型
@State unionState: number | undefined = 0;
/// provide
@Provide provideState: number = 0;
@Provide provideClassState: StateClass = new StateClass('provideClassState');
@Provide provideInterfaceState: StateInterface = { value: 'provideInterfaceState' };
@Provide provideArrayState: StateClass[] = [new StateClass('provideArrayState')];
@Provide provideDateState: Date = new Date('2021-08-08');
@Provide provideMapState: Map<number, string> = new Map([[0, "provideMapState"]]);
@Provide provideSetState: Set<number> = new Set([0]);
@Provide provideUnionState: number | undefined = 0;
/// object link
@State objectLinkClassB: ClassB = new ClassB(new ClassA(10))
@State objectLinkClassAs: ClassA[] = [new ClassA(1), new ClassA(2), new ClassA(3)];
@State objectLinkClassA: ClassA = new ClassA(100)
onCountUpdated() {
}
aboutToAppear(): void {
const localStoragePropSimpleValue: SubscribedAbstractProperty<string> = storage.prop('localStoragePropSimpleValue')
localStoragePropSimpleValue.set('localStoragePropSimpleValue_AboutToAppear')
const localStorageLinkObject: SubscribedAbstractProperty<StorageDemoClass> = storage.link('localStorageObject')
localStorageLinkObject.get().name = 'localStorageObject_AboutToAppear'
}
build() {
Column() {
Text(`@State simpleState: ${this.simpleState}`)
Text(`@State classState: ${this.classState.value}`)
Text(`@State interfaceState: ${this.interfaceState.value}`)
Text(`@State arrayState: ${this.arrayState.map(item => item.value).join(',')}`)
Text(`@State dateState: ${this.dateState}`)
Text(`@State mapState: ${Array.from(this.mapState).map((kv: [number, string]) => `${kv[0]}:${kv[1]}`).join(',')}`)
Text(`@State setState: ${Array.from(this.setState).join(',')}`)
Text(`@State unionState: ${this.unionState}`)
PropChildCmpt({
simpleProp: this.simpleState,
classProp: this.classState,
classValueProp: this.classState.value,
interfaceProp: this.interfaceState,
arrayProp: this.arrayState,
array0IndexValue: this.arrayState[0].value,
array0IndexProp: this.arrayState[0],
dateProp: this.dateState,
mapProp: this.mapState,
setProp: this.setState,
unionProp: this.unionState
})
LinkChildCmpt({
simpleLink: this.simpleState,
classLink: this.classState,
classValueLink: this.classState.value,
interfaceLink: this.interfaceState,
arrayLink: this.arrayState,
array0IndexValue: this.arrayState[0].value,
array0IndexLink: this.arrayState[0],
dateLink: this.dateState,
mapLink: this.mapState,
setLink: this.setState,
unionLink: this.unionState
})
ObjectLinkCmpt({
myMap: this.objectLinkClassA.myMap,
mySet: this.objectLinkClassA.mySet,
nestedClassA: this.objectLinkClassB.classA,
classAAtIndex0: this.objectLinkClassAs[0]
})
// 第一个参数是命名参数Param,里面传递参数名称,第二个参数是storage
LocalStorageCmpt({}, storage)
AppStorageCmpt({}, storage)
WatchChildCmpt()
TwoWayCmpt()
}
}
}
装换后代码
if (!("finalizeConstruction" in ViewPU.prototype)) {
Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { });
}
interface StateDemoPage_Params {
simpleState?: number;
classState?: StateClass;
interfaceState?: StateInterface;
arrayState?: StateClass[];
dateState?: Date;
mapState?: Map<number, string>;
setState?: Set<number>;
unionState?: number | undefined;
provideState?: number;
provideClassState?: StateClass;
provideInterfaceState?: StateInterface;
provideArrayState?: StateClass[];
provideDateState?: Date;
provideMapState?: Map<number, string>;
provideSetState?: Set<number>;
provideUnionState?: number | undefined;
objectLinkClassB?: ClassB;
objectLinkClassAs?: ClassA[];
objectLinkClassA?: ClassA;
}
import { PropChildCmpt } from "@bundle:com.unravel.myapplication/entry/ets/pages/PropChildCmpt";
import { LinkChildCmpt } from "@bundle:com.unravel.myapplication/entry/ets/pages/LinkChildCmpt";
import { ClassA, ClassB, ObjectLinkCmpt } from "@bundle:com.unravel.myapplication/entry/ets/pages/ObjectLinkCmpt";
import { LocalStorageCmpt } from "@bundle:com.unravel.myapplication/entry/ets/pages/LocalStorageCmpt";
import { AppStorageCmpt } from "@bundle:com.unravel.myapplication/entry/ets/pages/AppStorageCmpt";
import { WatchChildCmpt } from "@bundle:com.unravel.myapplication/entry/ets/pages/WatchChildCmpt";
import { TwoWayCmpt } from "@bundle:com.unravel.myapplication/entry/ets/pages/TwoWayCmpt";
export class StateClass {
public value: string;
constructor(value: string) {
this.value = value;
}
}
export interface StateInterface {
value: string;
}
export class StorageDemoClass {
name: string;
age: number;
constructor(name: string = '', age: number = 10) {
this.name = name;
this.age = age;
}
}
// LocalStorage可以观察到基本类型、class、Object、Map、Set、Date等的属性变化
let storage: LocalStorage = new LocalStorage();
storage.setOrCreate('localStorageSimpleValue', 47);
storage.setOrCreate('localStorageObject', new StorageDemoClass());
class StateDemoPage extends ViewPU {
constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) {
super(parent, __localStorage, elmtId, extraInfo);
if (typeof paramsLambda === "function") {
this.paramsGenerator_ = paramsLambda;
}
this.__simpleState = new ObservedPropertySimplePU(0, this, "simpleState");
this.__classState = new ObservedPropertyObjectPU(new StateClass('Hello'), this, "classState");
this.__interfaceState = new ObservedPropertyObjectPU({ value: 'World' }, this, "interfaceState");
this.__arrayState = new ObservedPropertyObjectPU([new StateClass('a'), new StateClass('b')], this, "arrayState");
this.__dateState = new ObservedPropertyObjectPU(new Date('2021-08-08')
// map类型
, this, "dateState");
this.__mapState = new ObservedPropertyObjectPU(new Map([[0, "a"], [1, "b"], [3, "c"]])
// Set类型
, this, "mapState");
this.__setState = new ObservedPropertyObjectPU(new Set([0, 1, 2, 3, 4])
// 联合类型
, this, "setState");
this.__unionState = new ObservedPropertyObjectPU(0, this, "unionState");
this.__provideState = new ObservedPropertySimplePU(0, this, "provideState");
this.addProvidedVar("provideState", this.__provideState, false);
this.__provideClassState = new ObservedPropertyObjectPU(new StateClass('provideClassState'), this, "provideClassState");
this.addProvidedVar("provideClassState", this.__provideClassState, false);
this.__provideInterfaceState = new ObservedPropertyObjectPU({ value: 'provideInterfaceState' }, this, "provideInterfaceState");
this.addProvidedVar("provideInterfaceState", this.__provideInterfaceState, false);
this.__provideArrayState = new ObservedPropertyObjectPU([new StateClass('provideArrayState')], this, "provideArrayState");
this.addProvidedVar("provideArrayState", this.__provideArrayState, false);
this.__provideDateState = new ObservedPropertyObjectPU(new Date('2021-08-08'), this, "provideDateState");
this.addProvidedVar("provideDateState", this.__provideDateState, false);
this.__provideMapState = new ObservedPropertyObjectPU(new Map([[0, "provideMapState"]]), this, "provideMapState");
this.addProvidedVar("provideMapState", this.__provideMapState, false);
this.__provideSetState = new ObservedPropertyObjectPU(new Set([0]), this, "provideSetState");
this.addProvidedVar("provideSetState", this.__provideSetState, false);
this.__provideUnionState = new ObservedPropertyObjectPU(0, this, "provideUnionState");
this.addProvidedVar("provideUnionState", this.__provideUnionState, false);
this.__objectLinkClassB = new ObservedPropertyObjectPU(new ClassB(new ClassA(10)), this, "objectLinkClassB");
this.__objectLinkClassAs = new ObservedPropertyObjectPU([new ClassA(1), new ClassA(2), new ClassA(3)], this, "objectLinkClassAs");
this.__objectLinkClassA = new ObservedPropertyObjectPU(new ClassA(100), this, "objectLinkClassA");
this.setInitiallyProvidedValue(params);
this.finalizeConstruction();
}
setInitiallyProvidedValue(params: StateDemoPage_Params) {
if (params.simpleState !== undefined) {
this.simpleState = params.simpleState;
}
if (params.classState !== undefined) {
this.classState = params.classState;
}
if (params.interfaceState !== undefined) {
this.interfaceState = params.interfaceState;
}
if (params.arrayState !== undefined) {
this.arrayState = params.arrayState;
}
if (params.dateState !== undefined) {
this.dateState = params.dateState;
}
if (params.mapState !== undefined) {
this.mapState = params.mapState;
}
if (params.setState !== undefined) {
this.setState = params.setState;
}
if (params.unionState !== undefined) {
this.unionState = params.unionState;
}
if (params.provideState !== undefined) {
this.provideState = params.provideState;
}
if (params.provideClassState !== undefined) {
this.provideClassState = params.provideClassState;
}
if (params.provideInterfaceState !== undefined) {
this.provideInterfaceState = params.provideInterfaceState;
}
if (params.provideArrayState !== undefined) {
this.provideArrayState = params.provideArrayState;
}
if (params.provideDateState !== undefined) {
this.provideDateState = params.provideDateState;
}
if (params.provideMapState !== undefined) {
this.provideMapState = params.provideMapState;
}
if (params.provideSetState !== undefined) {
this.provideSetState = params.provideSetState;
}
if (params.provideUnionState !== undefined) {
this.provideUnionState = params.provideUnionState;
}
if (params.objectLinkClassB !== undefined) {
this.objectLinkClassB = params.objectLinkClassB;
}
if (params.objectLinkClassAs !== undefined) {
this.objectLinkClassAs = params.objectLinkClassAs;
}
if (params.objectLinkClassA !== undefined) {
this.objectLinkClassA = params.objectLinkClassA;
}
}
updateStateVars(params: StateDemoPage_Params) {
}
purgeVariableDependenciesOnElmtId(rmElmtId) {
this.__simpleState.purgeDependencyOnElmtId(rmElmtId);
this.__classState.purgeDependencyOnElmtId(rmElmtId);
this.__interfaceState.purgeDependencyOnElmtId(rmElmtId);
this.__arrayState.purgeDependencyOnElmtId(rmElmtId);
this.__dateState.purgeDependencyOnElmtId(rmElmtId);
this.__mapState.purgeDependencyOnElmtId(rmElmtId);
this.__setState.purgeDependencyOnElmtId(rmElmtId);
this.__unionState.purgeDependencyOnElmtId(rmElmtId);
this.__provideState.purgeDependencyOnElmtId(rmElmtId);
this.__provideClassState.purgeDependencyOnElmtId(rmElmtId);
this.__provideInterfaceState.purgeDependencyOnElmtId(rmElmtId);
this.__provideArrayState.purgeDependencyOnElmtId(rmElmtId);
this.__provideDateState.purgeDependencyOnElmtId(rmElmtId);
this.__provideMapState.purgeDependencyOnElmtId(rmElmtId);
this.__provideSetState.purgeDependencyOnElmtId(rmElmtId);
this.__provideUnionState.purgeDependencyOnElmtId(rmElmtId);
this.__objectLinkClassB.purgeDependencyOnElmtId(rmElmtId);
this.__objectLinkClassAs.purgeDependencyOnElmtId(rmElmtId);
this.__objectLinkClassA.purgeDependencyOnElmtId(rmElmtId);
}
aboutToBeDeleted() {
this.__simpleState.aboutToBeDeleted();
this.__classState.aboutToBeDeleted();
this.__interfaceState.aboutToBeDeleted();
this.__arrayState.aboutToBeDeleted();
this.__dateState.aboutToBeDeleted();
this.__mapState.aboutToBeDeleted();
this.__setState.aboutToBeDeleted();
this.__unionState.aboutToBeDeleted();
this.__provideState.aboutToBeDeleted();
this.__provideClassState.aboutToBeDeleted();
this.__provideInterfaceState.aboutToBeDeleted();
this.__provideArrayState.aboutToBeDeleted();
this.__provideDateState.aboutToBeDeleted();
this.__provideMapState.aboutToBeDeleted();
this.__provideSetState.aboutToBeDeleted();
this.__provideUnionState.aboutToBeDeleted();
this.__objectLinkClassB.aboutToBeDeleted();
this.__objectLinkClassAs.aboutToBeDeleted();
this.__objectLinkClassA.aboutToBeDeleted();
SubscriberManager.Get().delete(this.id__());
this.aboutToBeDeletedInternal();
}
// 简单类型
private __simpleState: ObservedPropertySimplePU<number>;
get simpleState() {
return this.__simpleState.get();
}
set simpleState(newValue: number) {
this.__simpleState.set(newValue);
}
// class类型
private __classState: ObservedPropertyObjectPU<StateClass>;
get classState() {
return this.__classState.get();
}
set classState(newValue: StateClass) {
this.__classState.set(newValue);
}
// interface类型
private __interfaceState: ObservedPropertyObjectPU<StateInterface>;
get interfaceState() {
return this.__interfaceState.get();
}
set interfaceState(newValue: StateInterface) {
this.__interfaceState.set(newValue);
}
// 数组类型
private __arrayState: ObservedPropertyObjectPU<StateClass[]>;
get arrayState() {
return this.__arrayState.get();
}
set arrayState(newValue: StateClass[]) {
this.__arrayState.set(newValue);
}
// 日期类型
private __dateState: ObservedPropertyObjectPU<Date>;
get dateState() {
return this.__dateState.get();
}
set dateState(newValue: Date) {
this.__dateState.set(newValue);
}
// map类型
private __mapState: ObservedPropertyObjectPU<Map<number, string>>;
get mapState() {
return this.__mapState.get();
}
set mapState(newValue: Map<number, string>) {
this.__mapState.set(newValue);
}
// Set类型
private __setState: ObservedPropertyObjectPU<Set<number>>;
get setState() {
return this.__setState.get();
}
set setState(newValue: Set<number>) {
this.__setState.set(newValue);
}
// 联合类型
private __unionState: ObservedPropertyObjectPU<number | undefined>;
get unionState() {
return this.__unionState.get();
}
set unionState(newValue: number | undefined) {
this.__unionState.set(newValue);
}
/// provide
private __provideState: ObservedPropertySimplePU<number>;
get provideState() {
return this.__provideState.get();
}
set provideState(newValue: number) {
this.__provideState.set(newValue);
}
private __provideClassState: ObservedPropertyObjectPU<StateClass>;
get provideClassState() {
return this.__provideClassState.get();
}
set provideClassState(newValue: StateClass) {
this.__provideClassState.set(newValue);
}
private __provideInterfaceState: ObservedPropertyObjectPU<StateInterface>;
get provideInterfaceState() {
return this.__provideInterfaceState.get();
}
set provideInterfaceState(newValue: StateInterface) {
this.__provideInterfaceState.set(newValue);
}
private __provideArrayState: ObservedPropertyObjectPU<StateClass[]>;
get provideArrayState() {
return this.__provideArrayState.get();
}
set provideArrayState(newValue: StateClass[]) {
this.__provideArrayState.set(newValue);
}
private __provideDateState: ObservedPropertyObjectPU<Date>;
get provideDateState() {
return this.__provideDateState.get();
}
set provideDateState(newValue: Date) {
this.__provideDateState.set(newValue);
}
private __provideMapState: ObservedPropertyObjectPU<Map<number, string>>;
get provideMapState() {
return this.__provideMapState.get();
}
set provideMapState(newValue: Map<number, string>) {
this.__provideMapState.set(newValue);
}
private __provideSetState: ObservedPropertyObjectPU<Set<number>>;
get provideSetState() {
return this.__provideSetState.get();
}
set provideSetState(newValue: Set<number>) {
this.__provideSetState.set(newValue);
}
private __provideUnionState: ObservedPropertyObjectPU<number | undefined>;
get provideUnionState() {
return this.__provideUnionState.get();
}
set provideUnionState(newValue: number | undefined) {
this.__provideUnionState.set(newValue);
}
/// object link
private __objectLinkClassB: ObservedPropertyObjectPU<ClassB>;
get objectLinkClassB() {
return this.__objectLinkClassB.get();
}
set objectLinkClassB(newValue: ClassB) {
this.__objectLinkClassB.set(newValue);
}
private __objectLinkClassAs: ObservedPropertyObjectPU<ClassA[]>;
get objectLinkClassAs() {
return this.__objectLinkClassAs.get();
}
set objectLinkClassAs(newValue: ClassA[]) {
this.__objectLinkClassAs.set(newValue);
}
private __objectLinkClassA: ObservedPropertyObjectPU<ClassA>;
get objectLinkClassA() {
return this.__objectLinkClassA.get();
}
set objectLinkClassA(newValue: ClassA) {
this.__objectLinkClassA.set(newValue);
}
onCountUpdated() {
}
aboutToAppear(): void {
const localStoragePropSimpleValue: SubscribedAbstractProperty<string> = storage.prop('localStoragePropSimpleValue');
localStoragePropSimpleValue.set('localStoragePropSimpleValue_AboutToAppear');
const localStorageLinkObject: SubscribedAbstractProperty<StorageDemoClass> = storage.link('localStorageObject');
localStorageLinkObject.get().name = 'localStorageObject_AboutToAppear';
}
initialRender() {
this.observeComponentCreation2((elmtId, isInitialRender) => {
Column.create();
}, Column);
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create(`@State simpleState: ${this.simpleState}`);
}, Text);
Text.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create(`@State classState: ${this.classState.value}`);
}, Text);
Text.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create(`@State interfaceState: ${this.interfaceState.value}`);
}, Text);
Text.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create(`@State arrayState: ${this.arrayState.map(item => item.value).join(',')}`);
}, Text);
Text.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create(`@State dateState: ${this.dateState}`);
}, Text);
Text.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create(`@State mapState: ${Array.from(ObservedObject.GetRawObject(this.mapState)).map((kv: [
number,
string
]) => `${kv[0]}:${kv[1]}`).join(',')}`);
}, Text);
Text.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create(`@State setState: ${Array.from(ObservedObject.GetRawObject(this.setState)).join(',')}`);
}, Text);
Text.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create(`@State unionState: ${this.unionState}`);
}, Text);
Text.pop();
{
this.observeComponentCreation2((elmtId, isInitialRender) => {
if (isInitialRender) {
let componentCall = new PropChildCmpt(this, {
simpleProp: this.simpleState,
classProp: this.classState,
classValueProp: this.classState.value,
interfaceProp: this.interfaceState,
arrayProp: this.arrayState,
array0IndexValue: this.arrayState[0].value,
array0IndexProp: this.arrayState[0],
dateProp: this.dateState,
mapProp: this.mapState,
setProp: this.setState,
unionProp: this.unionState
}, undefined, elmtId, () => { }, { page: "entry/src/main/ets/pages/StateDemoPage.ets", line: 93 });
ViewPU.create(componentCall);
let paramsLambda = () => {
return {
simpleProp: this.simpleState,
classProp: this.classState,
classValueProp: this.classState.value,
interfaceProp: this.interfaceState,
arrayProp: this.arrayState,
array0IndexValue: this.arrayState[0].value,
array0IndexProp: this.arrayState[0],
dateProp: this.dateState,
mapProp: this.mapState,
setProp: this.setState,
unionProp: this.unionState
};
};
componentCall.paramsGenerator_ = paramsLambda;
}
else {
this.updateStateVarsOfChildByElmtId(elmtId, {
simpleProp: this.simpleState,
classProp: this.classState,
classValueProp: this.classState.value,
interfaceProp: this.interfaceState,
arrayProp: this.arrayState,
array0IndexValue: this.arrayState[0].value,
array0IndexProp: this.arrayState[0],
dateProp: this.dateState,
mapProp: this.mapState,
setProp: this.setState,
unionProp: this.unionState
});
}
}, { name: "PropChildCmpt" });
}
{
this.observeComponentCreation2((elmtId, isInitialRender) => {
if (isInitialRender) {
let componentCall = new LinkChildCmpt(this, {
simpleLink: this.__simpleState,
classLink: this.__classState,
classValueLink: this.classState.value,
interfaceLink: this.__interfaceState,
arrayLink: this.__arrayState,
array0IndexValue: this.arrayState[0].value,
array0IndexLink: this.arrayState[0],
dateLink: this.__dateState,
mapLink: this.__mapState,
setLink: this.__setState,
unionLink: this.__unionState
}, undefined, elmtId, () => { }, { page: "entry/src/main/ets/pages/StateDemoPage.ets", line: 106 });
ViewPU.create(componentCall);
let paramsLambda = () => {
return {
simpleLink: this.simpleState,
classLink: this.classState,
classValueLink: this.classState.value,
interfaceLink: this.interfaceState,
arrayLink: this.arrayState,
array0IndexValue: this.arrayState[0].value,
array0IndexLink: this.arrayState[0],
dateLink: this.dateState,
mapLink: this.mapState,
setLink: this.setState,
unionLink: this.unionState
};
};
componentCall.paramsGenerator_ = paramsLambda;
}
else {
this.updateStateVarsOfChildByElmtId(elmtId, {});
}
}, { name: "LinkChildCmpt" });
}
{
this.observeComponentCreation2((elmtId, isInitialRender) => {
if (isInitialRender) {
let componentCall = new ObjectLinkCmpt(this, {
myMap: this.objectLinkClassA.myMap,
mySet: this.objectLinkClassA.mySet,
nestedClassA: this.objectLinkClassB.classA,
classAAtIndex0: this.objectLinkClassAs[0]
}, undefined, elmtId, () => { }, { page: "entry/src/main/ets/pages/StateDemoPage.ets", line: 119 });
ViewPU.create(componentCall);
let paramsLambda = () => {
return {
myMap: this.objectLinkClassA.myMap,
mySet: this.objectLinkClassA.mySet,
nestedClassA: this.objectLinkClassB.classA,
classAAtIndex0: this.objectLinkClassAs[0]
};
};
componentCall.paramsGenerator_ = paramsLambda;
}
else {
this.updateStateVarsOfChildByElmtId(elmtId, {
myMap: this.objectLinkClassA.myMap,
mySet: this.objectLinkClassA.mySet,
nestedClassA: this.objectLinkClassB.classA,
classAAtIndex0: this.objectLinkClassAs[0]
});
}
}, { name: "ObjectLinkCmpt" });
}
{
this.observeComponentCreation2((elmtId, isInitialRender) => {
if (isInitialRender) {
let componentCall = new
// 第一个参数是命名参数Param,里面传递参数名称,第二个参数是storage
LocalStorageCmpt(this, {}, storage, elmtId, () => { }, { page: "entry/src/main/ets/pages/StateDemoPage.ets", line: 126 });
ViewPU.create(componentCall);
let paramsLambda = () => {
return {};
};
componentCall.paramsGenerator_ = paramsLambda;
}
else {
this.updateStateVarsOfChildByElmtId(elmtId, {});
}
}, { name: "LocalStorageCmpt" });
}
{
this.observeComponentCreation2((elmtId, isInitialRender) => {
if (isInitialRender) {
let componentCall = new AppStorageCmpt(this, {}, storage, elmtId, () => { }, { page: "entry/src/main/ets/pages/StateDemoPage.ets", line: 127 });
ViewPU.create(componentCall);
let paramsLambda = () => {
return {};
};
componentCall.paramsGenerator_ = paramsLambda;
}
else {
this.updateStateVarsOfChildByElmtId(elmtId, {});
}
}, { name: "AppStorageCmpt" });
}
{
this.observeComponentCreation2((elmtId, isInitialRender) => {
if (isInitialRender) {
let componentCall = new WatchChildCmpt(this, {}, undefined, elmtId, () => { }, { page: "entry/src/main/ets/pages/StateDemoPage.ets", line: 128 });
ViewPU.create(componentCall);
let paramsLambda = () => {
return {};
};
componentCall.paramsGenerator_ = paramsLambda;
}
else {
this.updateStateVarsOfChildByElmtId(elmtId, {});
}
}, { name: "WatchChildCmpt" });
}
{
this.observeComponentCreation2((elmtId, isInitialRender) => {
if (isInitialRender) {
let componentCall = new TwoWayCmpt(this, {}, undefined, elmtId, () => { }, { page: "entry/src/main/ets/pages/StateDemoPage.ets", line: 129 });
ViewPU.create(componentCall);
let paramsLambda = () => {
return {};
};
componentCall.paramsGenerator_ = paramsLambda;
}
else {
this.updateStateVarsOfChildByElmtId(elmtId, {});
}
}, { name: "TwoWayCmpt" });
}
Column.pop();
}
rerender() {
this.updateDirtyElements();
}
static getEntryName(): string {
return "StateDemoPage";
}
}
{
registerNamedRoute(() => new StateDemoPage(undefined, {}), "", { bundleName: "com.unravel.myapplication", moduleName: "entry", pagePath: "pages/StateDemoPage" });
}
@State
转换前后
状态变量在转换成TS代码之后,原有变量会被重写为getter和setter,同时声明一个 双下划线开头的私有变量,getter和setter内访问的该私有变量的get和set方法
初始化
这个私有变量有两种形式 ObservedPropertySimplePU和ObservedPropertyObjectPU ,简单类型的状态变量会转换成ObservedPropertySimplePU,其余的都会转换成ObservedPropertyObjectPU
ObservedPropertyPU
我们搜索ObservedPropertySimplePU还是ObservedPropertyObjectPU,发现这两个类都是继承于 ObservedPropertyPU
至于怎么区分的,我们可以在gitee.com/openharmony…里找到代码
isSimpleType
查看一下 isSimpleType 方法的实现,isSimpleType调用了getDeclarationType,getDeclarationType里主要的是isBasicType
isBasicType
isBasicType里面判断了,如果是string、boolean、number、enum、bigin以及他们的包装类String、Boolean、Number等都是基础类型
小结
@State状态变量转换后会生成ObservedPropertyPU的子类,如果@State装饰的是简单类型string、number,boolean、enum等以及他们的包装类,最终会被转换成ObservedPropertySimplePU,其他情况会被转换成 ObservedPropertyObjectPU
传参
可以参考 鸿蒙ACE-ArkUI构建(二)、渲染控制和构建过程 initialRender:构建UI树 部分
结论:任何组件的创建函数 都被包裹在 observeComponentCreation2 函数中,这个函数内部会将组件创建函数包装成一个updateFunc函数并存储起来
传递的参数最终会通过对应组件的Create方法传入
ObservedPropertyPU 怎么关联UI?
可以看到,在生成状态变量的时候就已经将视图自身传递进去了。这样状态变量变化的时候,就能拿到组件的实例取,然后更新本组件内依赖状态变量的系统组件
ObservedPropertyPU类的定义如下,继承自ObservedPropertyAbstractPU,实现了PeerChangeEventReceiverPU和ObservedObjectEventsPUReceiver两个接口
ObservedPropertyPU
constructor(localInitValue: T, owningView: IPropertySubscriber, propertyName: PropertyInfo)
constructor中传入的owningView就是我们的组件实例this。继续查看super方法
ObservedPropertyAbstractPU
constructor(subscriber: IPropertySubscriber, viewName: PropertyInfo)
owningView_持有了状态变量所在的视图,当状态变量变化时就能拿到视图的实例。
关联使用状态变量的UI
ObservedPropertyPU
public get(): T
在访问状态变量的属性时,会通过get方法,在get方法中会记录依赖这个状态变量的elmtId
ObservedPropertyAbstractPU
protected recordPropertyDependentUpdate() : void
我们看到getRenderingElmtId中判断了有owningView_才会获取当前正在渲染的组件的ElmtId。这个owningView_正是判断是否是状态变量的条件
最终将elmtId 通过dependentElmtIdsByProperty_.addPropertyDependency方法保存了起来
ViewPU
public getCurrentlyRenderedElmtId()
取当前栈顶的elmtId,即当前正在渲染的组件的id
ObservedPropertyAbstractPU
private dependentElmtIdsByProperty_ = new PropertyDependencies();
可以看到dependentElmtIdsByProperty是一个PropertyDependencies的实例对象。我们简单看一下这个类里面的存储形式
PropertyDependencies
小结
- 状态变量创建的时候会将视图this传进去,在状态变量对象内部通过owningView持有了视图实例
- 在使用状态变量的时候,会将组件id存储在 dependentElmtIdsByProperty_中,这是一个用于存储依赖这个状态变量的组件id的集合,同时它也负责存储使用@Track属性的那些组件id集合
ObservedPropertyPU 怎么更新UI?
ObservedPropertyPU
private setValueInternal(newValue: T): boolean
创建组件的时候会调用setValueInternal,该方法内部会根据newValue的类型进行一系列的处理
public set(newValue: T): void
给状态变量赋值的时候会调用ObservedPropertyPU的set方法,那么更新UI的逻辑也会在这里。我们看一下
先调用了setValueInternal,然后通知依赖状态变量的组件进行更新
TrackedObject
public static notifyObjectValueAssignment(obj1: Object, obj2: Object, notifyPropertyChanged: () => void, // notify as assignment (none-optimised) notifyTrackedPropertyChange: (propName) => void, obSelf: ObservedPropertyAbstractPU): boolean
因为我们从上面setValueInternal看到,每次设置值相当于创建一个新的ObservedObject实例,这里使用obj1和obj2 constructor的三等判断就可以判断出是否对象发生了变化
notifyPropertyChanged.call(obSelf) 这里其实就是调用了obSelf的notifyPropertyChanged方法,而 notifyPropertyChanged传入的是this.notifyPropertyHasChangedPU。
ObservedPropertyAbstractPU
protected notifyPropertyHasChangedPU()
可以看到,最终是调用了对应组件实例的 viewPropertyHasChanged 方法
protected notifyTrackedObjectPropertyHasChanged(changedPropertyName : string) : void
ViewPU
viewPropertyHasChanged(varName: PropertyInfo, dependentElmtIds: Set): void
- 标记目前的UI节点是 dirty
- 记录需要更新的子组件的 id
之后PipelineContext会调用到组件的rerender方法
protected abstract rerender(): void
通过编译后的代码,我们可以看到重新渲染的时候调用了updateDirtyElements
public updateDirtyElements(): void
对dirtDescendantElementIds_ 排序后,调用了UpdateElement
对组件id进行升序排列是为了保证父节点会在其子节点之前被更新。我们知道组件树的创建是通过create、pop完成的,并且是栈结构,父组件会先入栈,子组件后入栈。
无论哪个组件,都会调用create方法,create方法内会生成组件唯一id并且是递增的,这样就保证了先入栈的组件id更小
public UpdateElement(elmtId: number): void
这个方法里面调用了updateFunc和finishUpdateFunc
updateFunc是不是有点熟悉,就是我们之前看的创建组件的时候observeComponentCreation2内包装的函数
小结
我们写的ArkTS代码经过转换后生成TS代码,创建组件的代码被包装进一个箭头函数传入observeComponentCreation2,然后内部又对传入的这个箭头函数做了一层包装,并且存储下来
之后状态变量发生变化的时候,就拿到它所持有的view,并通过elemId拿到存储下来的更新函数并且调用。
为什么@State状态变量只能观测一层变化
ObservedProeprtyPU
this.wrappedValue_ = ObservedObject.createNew(newValue, this);
回想setValueInternal的实现。我们进入最后一个分支。可以看到最终存储值的是wrappedValue_属性
ObservedObject
public static createNew(rawObject: T, owningProperty: IPropertySubscriber): T
createNew内部调用了createNewInternal,createNewInternal内部根据不同类型做了不同的处理。
不同的类型使用不同的Handler
public static addOwningProperty(obj: Object, subscriber: IPropertySubscriber): boolean
最终所有的状态变量都是一个ObservedObject对象,这个对象有一个Symbol('___subscribe') 指向原始的属性
ExtendableProxy
同时它继承自ExtendableProxy,返回一个Proxy,这个实例代理了状态变量的get和set
SubscribableMapSetHandler
可以看到Map、Set等作为状态变量时,只有调用set、clear、delete才会驱动UI更新,其他方法不会驱动UI
我们看下get方法的签名是不是很熟悉,和属性装饰器非常相似,虽然相似,但它其实是Proxy的一个方法
SubscribableDateHandler
和Map、Set类似,只有调用特定API时才会驱动UI更新
SubscribableArrayHandler
Array作为状态变量时和Map、Set类似,只有特定API才能驱动UI更新。
官网最新版,其实还支持push,pop等操作
developer.huawei.com/consumer/cn…
SubscribableHandler
在 TrackedObject.isCompatibilityMode(target) 为true时就通知属性变化
TrackedObject
public static isCompatibilityMode(obj: Object): boolean
不存在或者非对象或者非@Track装饰的属性所属的类的实例对象,就返回true
SubscribableHandler
protected notifyObjectPropertyHasChanged(propName: string, newValue: any)
最终调用到了owningProperty属性的onTrackedObjectPropertyCompatModeHasChangedPU、hasChange、propertyHasChanged等方法
ObservedPropertyAbstractPU
public onTrackedObjectPropertyCompatModeHasChangedPU(sourceObject: ObservedObject, changedPropertyName: string)
onTrackedObjectPropertyCompatModeHasChangedPU这个方法是状态变量所属类ObservedPropertyPU 的父类 ObservedPropertyAbstractPU 中的方法
onTrackedObjectPropertyCompatModeHasChangedPU这个方法内部又调用了notifyPropertyHasChangedPU。notifyPropertyHasChangedPU上面已经分析过了,是实际驱动UI更新的方法
protected notifyPropertyHasChangedPU()
根据持有的视图view属性 owningView_,调用视图上的viewPropertyHasChanged属性
ViewPU
viewPropertyHasChanged(varName: PropertyInfo, dependentElmtIds: Set): void
小结
- 所有的@State状态变量最终都会被转换成一个ObservedObject对象,ObservedObject又是一个Proxy。所以所有状态变量最终都会被代理
- Proxy拦截了所有属性的get和set访问,因为Proxy只能拦截对象的的所有一级属性,所以状态变量只能观察到自身以及自身以及属性的变化
- 在访问get的时候,会记录使用状态变量的组件的elmtId,从而能在状态变量变化的时候驱动使用状态变量的UI进行更新
@State 状态的一个属性变化,是依赖@State的所有组件都更新,还是只有依赖这个属性的组件更新
ObservedPropertyAbstractPU
protected notifyPropertyHasChangedPU()
更新组件的上一步是notifyPropertyHasChangedPU。这个方法里面第二个参数传递了需要更新的组件的id的集合
PropertyDependencies
this.dependentElmtIdsByProperty_.getAllPropertyDependencies()
这里只是返回了propertyDependencies_。继续看下propertyDependencies_添加数据的地方
ObservedPropertyAbstractPU
protected recordPropertyDependentUpdate() : void
最终找到了recordPropertyDependentUpdate
在get方法中调用的 recordPropertyDependentUpdate,后续可以回到上面查看 关联使用状态变量的UI 章节
ViewPU
public observeComponentCreation2(compilerAssignedUpdateFunc: UpdateFunc, classObject: UIClassObject): void
回头看下observeComponentCreation2。这个函数内部每次update的时候都会讲elmid入栈,出栈。
所以数组最后一个,就是栈顶,即当前正在渲染的组件
小结
-
getAllPropertyDependencies 获取的是所有依赖这个状态变量的组件
- 状态上的某个属性变化的时候,会导致依赖这个状态变量的所有组件都更新
总结
-
@State装饰的变量经过编译后会被重写为getter和setter,同时生成一个以双下划线开头的类型为ObservedPropertyPU的私有属性,getter和setter里就是访问的这个私有属性的get和set方法
- 组件使用状态变量时会通过getter调用到ObservedPropertyPU的get方法,在get方法里会将使用这个状态变量的组件的id存储到一个Set中
- 我们修改状态变量时会调用setter方法进而调用到ObserverPropertyPU的set方法,set方法内会通过 getAllPropertyDependencies 获取依赖这个状态变量的所有的组件id,然后将这些组件标记为dirty
-
创建组件时,每个组件的创建函数都会被observeComponentCreation2包裹,创建函数被包装成一个updateFunc同时存储在Map中。Map中key为组件的id,value为updateFunc
-
每一个@State状态变量通过owningView_持有使用它所属的视图,状态变量变化时可以通过owningView_拿到对应id的子组件
- 状态变量变化时可以通过owningView_拿到所属组件的实例,然后将getAllPropertyDependencies获取的依赖于这个状态变量的所有组件id集合作为参数,调用 viewPropertyHasChanged将这些组件标记为dirty
- 然后经过一些列的调用之后会调用到组件的UpdateElement函数,这个函数内部会通过传过来的子组件id拿到子组件对应的更新函数updateFunc,然后调用updateFun完成UI刷新
图示
ArkUI中通过修改 和view 绑的的状态变量 来标记 view dirty,并且将UI树上需要重新绘制的区域以及子组件的id记录下来,后续 vsync 信号后,会进行 rerender,来更新UI状态