鸿蒙ACE-V1状态分析@State

236 阅读14分钟

@State装饰器:组件内状态

developer.huawei.com/consumer/cn…

初始化规则如下图

image.png

@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方法

image.png

初始化

这个私有变量有两种形式 ObservedPropertySimplePUObservedPropertyObjectPU ,简单类型的状态变量会转换成ObservedPropertySimplePU,其余的都会转换成ObservedPropertyObjectPU

image.png

ObservedPropertyPU

我们搜索ObservedPropertySimplePU还是ObservedPropertyObjectPU,发现这两个类都是继承于 ObservedPropertyPU

image.png

至于怎么区分的,我们可以在gitee.com/openharmony…里找到代码

image.png

image.png

isSimpleType

查看一下 isSimpleType 方法的实现,isSimpleType调用了getDeclarationType,getDeclarationType里主要的是isBasicType

image.png

isBasicType

isBasicType里面判断了,如果是string、boolean、number、enum、bigin以及他们的包装类String、Boolean、Number等都是基础类型

image.png

小结

@State状态变量转换后会生成ObservedPropertyPU的子类,如果@State装饰的是简单类型string、number,boolean、enum等以及他们的包装类,最终会被转换成ObservedPropertySimplePU,其他情况会被转换成 ObservedPropertyObjectPU

传参

可以参考 鸿蒙ACE-ArkUI构建(二)、渲染控制和构建过程  initialRender:构建UI树  部分

结论:任何组件的创建函数 都被包裹在 observeComponentCreation2 函数中,这个函数内部会将组件创建函数包装成一个updateFunc函数并存储起来

传递的参数最终会通过对应组件的Create方法传入

image.png

ObservedPropertyPU 怎么关联UI?

可以看到,在生成状态变量的时候就已经将视图自身传递进去了。这样状态变量变化的时候,就能拿到组件的实例取,然后更新本组件内依赖状态变量的系统组件

image.png

ObservedPropertyPU类的定义如下,继承自ObservedPropertyAbstractPU,实现了PeerChangeEventReceiverPU和ObservedObjectEventsPUReceiver两个接口

ObservedPropertyPU

constructor(localInitValue: T, owningView: IPropertySubscriber, propertyName: PropertyInfo)

image.png

constructor中传入的owningView就是我们的组件实例this。继续查看super方法

ObservedPropertyAbstractPU

constructor(subscriber: IPropertySubscriber, viewName: PropertyInfo)

image.png

owningView_持有了状态变量所在的视图,当状态变量变化时就能拿到视图的实例。

关联使用状态变量的UI

ObservedPropertyPU

public get(): T

在访问状态变量的属性时,会通过get方法,在get方法中会记录依赖这个状态变量的elmtId

image.png

ObservedPropertyAbstractPU

protected recordPropertyDependentUpdate() : void

我们看到getRenderingElmtId中判断了有owningView_才会获取当前正在渲染的组件的ElmtId。这个owningView_正是判断是否是状态变量的条件

最终将elmtId 通过dependentElmtIdsByProperty_.addPropertyDependency方法保存了起来

image.png

ViewPU

public getCurrentlyRenderedElmtId()

取当前栈顶的elmtId,即当前正在渲染的组件的id

image.png

ObservedPropertyAbstractPU

private dependentElmtIdsByProperty_ = new PropertyDependencies();

可以看到dependentElmtIdsByProperty是一个PropertyDependencies的实例对象。我们简单看一下这个类里面的存储形式

PropertyDependencies

image.png

小结

  1. 状态变量创建的时候会将视图this传进去,在状态变量对象内部通过owningView持有了视图实例
  2. 在使用状态变量的时候,会将组件id存储在 dependentElmtIdsByProperty_中,这是一个用于存储依赖这个状态变量的组件id的集合,同时它也负责存储使用@Track属性的那些组件id集合

ObservedPropertyPU 怎么更新UI?

ObservedPropertyPU

private setValueInternal(newValue: T): boolean

创建组件的时候会调用setValueInternal,该方法内部会根据newValue的类型进行一系列的处理

image.png

public set(newValue: T): void

给状态变量赋值的时候会调用ObservedPropertyPU的set方法,那么更新UI的逻辑也会在这里。我们看一下

先调用了setValueInternal,然后通知依赖状态变量的组件进行更新

image.png

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。

image.png

ObservedPropertyAbstractPU

protected notifyPropertyHasChangedPU()

可以看到,最终是调用了对应组件实例的 viewPropertyHasChanged 方法

image.png

protected notifyTrackedObjectPropertyHasChanged(changedPropertyName : string) : void

image.png

ViewPU

viewPropertyHasChanged(varName: PropertyInfo, dependentElmtIds: Set): void

  • 标记目前的UI节点是 dirty
  • 记录需要更新的子组件的 id

image.png

之后PipelineContext会调用到组件的rerender方法

protected abstract rerender(): void

通过编译后的代码,我们可以看到重新渲染的时候调用了updateDirtyElements

image.png

public updateDirtyElements(): void

对dirtDescendantElementIds_ 排序后,调用了UpdateElement

对组件id进行升序排列是为了保证父节点会在其子节点之前被更新。我们知道组件树的创建是通过create、pop完成的,并且是栈结构,父组件会先入栈,子组件后入栈。

无论哪个组件,都会调用create方法,create方法内会生成组件唯一id并且是递增的,这样就保证了先入栈的组件id更小

image.png

public UpdateElement(elmtId: number): void

image.png 这个方法里面调用了updateFunc和finishUpdateFunc

updateFunc是不是有点熟悉,就是我们之前看的创建组件的时候observeComponentCreation2内包装的函数

image.png

小结

我们写的ArkTS代码经过转换后生成TS代码,创建组件的代码被包装进一个箭头函数传入observeComponentCreation2,然后内部又对传入的这个箭头函数做了一层包装,并且存储下来

之后状态变量发生变化的时候,就拿到它所持有的view,并通过elemId拿到存储下来的更新函数并且调用。

image.png

为什么@State状态变量只能观测一层变化

ObservedProeprtyPU

this.wrappedValue_ = ObservedObject.createNew(newValue, this);

回想setValueInternal的实现。我们进入最后一个分支。可以看到最终存储值的是wrappedValue_属性

image.png

ObservedObject

public static createNew(rawObject: T, owningProperty: IPropertySubscriber): T

createNew内部调用了createNewInternal,createNewInternal内部根据不同类型做了不同的处理。

不同的类型使用不同的Handler

image.png

public static addOwningProperty(obj: Object, subscriber: IPropertySubscriber): boolean

最终所有的状态变量都是一个ObservedObject对象,这个对象有一个Symbol('___subscribe') 指向原始的属性

image.png

ExtendableProxy

同时它继承自ExtendableProxy,返回一个Proxy,这个实例代理了状态变量的get和set

image.png

SubscribableMapSetHandler

可以看到Map、Set等作为状态变量时,只有调用set、clear、delete才会驱动UI更新,其他方法不会驱动UI

我们看下get方法的签名是不是很熟悉,和属性装饰器非常相似,虽然相似,但它其实是Proxy的一个方法

image.png

SubscribableDateHandler

和Map、Set类似,只有调用特定API时才会驱动UI更新

image.png

SubscribableArrayHandler

Array作为状态变量时和Map、Set类似,只有特定API才能驱动UI更新。

官网最新版,其实还支持push,pop等操作

developer.huawei.com/consumer/cn…

image.png

SubscribableHandler

image.png 在 TrackedObject.isCompatibilityMode(target) 为true时就通知属性变化

TrackedObject

public static isCompatibilityMode(obj: Object): boolean

不存在或者非对象或者非@Track装饰的属性所属的类的实例对象,就返回true

image.png

SubscribableHandler

protected notifyObjectPropertyHasChanged(propName: string, newValue: any)

最终调用到了owningProperty属性的onTrackedObjectPropertyCompatModeHasChangedPU、hasChange、propertyHasChanged等方法

image.png

ObservedPropertyAbstractPU

public onTrackedObjectPropertyCompatModeHasChangedPU(sourceObject: ObservedObject, changedPropertyName: string)

onTrackedObjectPropertyCompatModeHasChangedPU这个方法是状态变量所属类ObservedPropertyPU 的父类 ObservedPropertyAbstractPU 中的方法

onTrackedObjectPropertyCompatModeHasChangedPU这个方法内部又调用了notifyPropertyHasChangedPU。notifyPropertyHasChangedPU上面已经分析过了,是实际驱动UI更新的方法

image.png

protected notifyPropertyHasChangedPU()

根据持有的视图view属性 owningView_,调用视图上的viewPropertyHasChanged属性

image.png

ViewPU

viewPropertyHasChanged(varName: PropertyInfo, dependentElmtIds: Set): void

image.png

小结

  1. 所有的@State状态变量最终都会被转换成一个ObservedObject对象,ObservedObject又是一个Proxy。所以所有状态变量最终都会被代理
  2. Proxy拦截了所有属性的get和set访问,因为Proxy只能拦截对象的的所有一级属性,所以状态变量只能观察到自身以及自身以及属性的变化
  3. 在访问get的时候,会记录使用状态变量的组件的elmtId,从而能在状态变量变化的时候驱动使用状态变量的UI进行更新

image.png

@State 状态的一个属性变化,是依赖@State的所有组件都更新,还是只有依赖这个属性的组件更新

ObservedPropertyAbstractPU

protected notifyPropertyHasChangedPU()

更新组件的上一步是notifyPropertyHasChangedPU。这个方法里面第二个参数传递了需要更新的组件的id的集合

image.png

PropertyDependencies

this.dependentElmtIdsByProperty_.getAllPropertyDependencies()

这里只是返回了propertyDependencies_。继续看下propertyDependencies_添加数据的地方

image.png

image.png

ObservedPropertyAbstractPU

protected recordPropertyDependentUpdate() : void

最终找到了recordPropertyDependentUpdate

image.png

在get方法中调用的 recordPropertyDependentUpdate,后续可以回到上面查看 关联使用状态变量的UI 章节

ViewPU

public observeComponentCreation2(compilerAssignedUpdateFunc: UpdateFunc, classObject: UIClassObject): void

回头看下observeComponentCreation2。这个函数内部每次update的时候都会讲elmid入栈,出栈。

所以数组最后一个,就是栈顶,即当前正在渲染的组件

image.png

小结

  1. getAllPropertyDependencies 获取的是所有依赖这个状态变量的组件

  2. 状态上的某个属性变化的时候,会导致依赖这个状态变量的所有组件都更新

总结

  1. @State装饰的变量经过编译后会被重写为getter和setter,同时生成一个以双下划线开头的类型为ObservedPropertyPU的私有属性,getter和setter里就是访问的这个私有属性的get和set方法

    1. 组件使用状态变量时会通过getter调用到ObservedPropertyPU的get方法,在get方法里会将使用这个状态变量的组件的id存储到一个Set中
    2. 我们修改状态变量时会调用setter方法进而调用到ObserverPropertyPU的set方法,set方法内会通过 getAllPropertyDependencies 获取依赖这个状态变量的所有的组件id,然后将这些组件标记为dirty
  2. 创建组件时,每个组件的创建函数都会被observeComponentCreation2包裹,创建函数被包装成一个updateFunc同时存储在Map中。Map中key为组件的id,value为updateFunc

  3. 每一个@State状态变量通过owningView_持有使用它所属的视图,状态变量变化时可以通过owningView_拿到对应id的子组件

    1. 状态变量变化时可以通过owningView_拿到所属组件的实例,然后将getAllPropertyDependencies获取的依赖于这个状态变量的所有组件id集合作为参数,调用 viewPropertyHasChanged将这些组件标记为dirty
    2. 然后经过一些列的调用之后会调用到组件的UpdateElement函数,这个函数内部会通过传过来的子组件id拿到子组件对应的更新函数updateFunc,然后调用updateFun完成UI刷新

图示

image.png

ArkUI中通过修改 和view 绑的的状态变量 来标记 view dirty,并且将UI树上需要重新绘制的区域以及子组件的id记录下来,后续 vsync 信号后,会进行 rerender,来更新UI状态

image.png

参考资料

  1. wangdoc.com/es6/proxy
  2. gitee.com/openharmony…
  3. 声明式范式的语法编译转换,语法验证 https://gitee.com/openharmony/developtools_ace_ets2bundle