鸿蒙ACE-V1状态分析@Provide、@Consume

96 阅读5分钟

@Provide装饰器和@Consume装饰器:与后代组件双向同步

developer.huawei.com/consumer/cn…

@Provide初始化规则图示

@Consume初始化规则图示

框架行为

  1. 初始渲染:

    1. @Provide装饰的变量会以map的形式,传递给当前@Provide所属组件的所有子组件
    2. 子组件中如果使用@Consume变量,则会在map中查找是否有该变量名/alias(别名)对应的@Provide的变量,如果查找不到,框架会抛出JS ERROR;
    3. 在初始化@Consume变量时,和@State/@Link的流程类似,@Consume变量会保存 在map中查找到的@Provide变量,并把自己注册给@Provide。
  2. 当@Provide装饰的数据变化时:

    1. 通过初始渲染的步骤可知,子组件@Consume已把自己注册给父组件。父组件@Provide变量变更后,会遍历更新所有依赖它的系统组件(elementid)和状态变量(@Consume);
    2. 通知@Consume更新后,子组件所有依赖@Consume的系统组件(elementId)都会被通知更新。以此实现@Provide对@Consume状态数据同步。
  3. 当@Consume装饰的数据变化时:

    通过初始渲染的步骤可知,子组件@Consume持有@Provide的实例。在@Consume更新后调用@Provide的更新方法,将更新的数值同步回@Provide,以此实现@Consume向@Provide的同步更新。

转换前代码

/**
 *
 * ConsumeChildCmpt.ets
 * Created by unravel on 2024/5/3
 * @abstract
 */
import { StateClass, StateInterface } from './StateDemoPage'
 
@Component
export struct ConsumeChildCmpt {
  // 简单类型
  @Consume provideSimpleState: number
  // class类型
  @Consume provideClassState: StateClass
  // interface类型
  @Consume provideInterfaceState: StateInterface
  // 数组类型
  @Consume provideArrayState: StateClass[]
  // 日期类型
  @Consume provideDateState: Date
  // 装饰Map类型变量
  @Consume provideMapState: Map<number, string>
  // 装饰Set类型变量
  @Consume provideSetState: Set<number>
  // 支持联合类型实例
  @Consume provideUnionState: number | undefined
 
  build() {
    Column() {
      Text(`@Consume provideSimpleState: ${this.provideSimpleState}`)
      Text(`@Consume provideClassState: ${this.provideClassState.value}`)
      Text(`@Consume provideInterfaceState: ${this.provideInterfaceState.value}`)
      Text(`@Consume provideArrayState: ${this.provideArrayState.map(item => item.value).join(',')}`)
      Text(`@Consume provideDateState: ${this.provideDateState}`)
      Text(`@Consume provideMapState: ${Array.from(this.provideMapState)
        .map((kv: [number, string]) => `${kv[0]}:${kv[1]}`)
        .join(',')}`)
      Text(`@Consume provideSetState: ${Array.from(this.provideSetState).join(',')}`)
      Text(`@Consume provideUnionState: ${this.provideUnionState}`)
    }
  }
}

转换后代码

if (!("finalizeConstruction" in ViewPU.prototype)) {
    Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { });
}
interface ConsumeChildCmpt_Params {
    provideSimpleState?: number;
    provideClassState?: StateClass;
    provideInterfaceState?: StateInterface;
    provideArrayState?: StateClass[];
    provideDateState?: Date;
    provideMapState?: Map<number, string>;
    provideSetState?: Set<number>;
    provideUnionState?: number | undefined;
}
import type { StateClass, StateInterface } from './StateDemoPage';
export class ConsumeChildCmpt extends ViewPU {
    constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) {
        super(parent, __localStorage, elmtId, extraInfo);
        if (typeof paramsLambda === "function") {
            this.paramsGenerator_ = paramsLambda;
        }
        this.__provideSimpleState = this.initializeConsume("provideSimpleState", "provideSimpleState");
        this.__provideClassState = this.initializeConsume("provideClassState", "provideClassState");
        this.__provideInterfaceState = this.initializeConsume("provideInterfaceState", "provideInterfaceState");
        this.__provideArrayState = this.initializeConsume("provideArrayState", "provideArrayState");
        this.__provideDateState = this.initializeConsume("provideDateState", "provideDateState");
        this.__provideMapState = this.initializeConsume("provideMapState", "provideMapState");
        this.__provideSetState = this.initializeConsume("provideSetState", "provideSetState");
        this.__provideUnionState = this.initializeConsume("provideUnionState", "provideUnionState");
        this.setInitiallyProvidedValue(params);
        this.finalizeConstruction();
    }
    setInitiallyProvidedValue(params: ConsumeChildCmpt_Params) {
    }
    updateStateVars(params: ConsumeChildCmpt_Params) {
    }
    purgeVariableDependenciesOnElmtId(rmElmtId) {
        this.__provideSimpleState.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);
    }
    aboutToBeDeleted() {
        this.__provideSimpleState.aboutToBeDeleted();
        this.__provideClassState.aboutToBeDeleted();
        this.__provideInterfaceState.aboutToBeDeleted();
        this.__provideArrayState.aboutToBeDeleted();
        this.__provideDateState.aboutToBeDeleted();
        this.__provideMapState.aboutToBeDeleted();
        this.__provideSetState.aboutToBeDeleted();
        this.__provideUnionState.aboutToBeDeleted();
        SubscriberManager.Get().delete(this.id__());
        this.aboutToBeDeletedInternal();
    }
    // 简单类型
    private __provideSimpleState: ObservedPropertyAbstractPU<number
    // class类型
    >;
    get provideSimpleState() {
        return this.__provideSimpleState.get();
    }
    set provideSimpleState(newValue: number) {
        this.__provideSimpleState.set(newValue);
    }
    // class类型
    private __provideClassState: ObservedPropertyAbstractPU<StateClass
    // interface类型
    >;
    get provideClassState() {
        return this.__provideClassState.get();
    }
    set provideClassState(newValue: StateClass) {
        this.__provideClassState.set(newValue);
    }
    // interface类型
    private __provideInterfaceState: ObservedPropertyAbstractPU<StateInterface
    // 数组类型
    >;
    get provideInterfaceState() {
        return this.__provideInterfaceState.get();
    }
    set provideInterfaceState(newValue: StateInterface) {
        this.__provideInterfaceState.set(newValue);
    }
    // 数组类型
    private __provideArrayState: ObservedPropertyAbstractPU<StateClass[]
    // 日期类型
    >;
    get provideArrayState() {
        return this.__provideArrayState.get();
    }
    set provideArrayState(newValue: StateClass[]) {
        this.__provideArrayState.set(newValue);
    }
    // 日期类型
    private __provideDateState: ObservedPropertyAbstractPU<Date
    // 装饰Map类型变量
    >;
    get provideDateState() {
        return this.__provideDateState.get();
    }
    set provideDateState(newValue: Date) {
        this.__provideDateState.set(newValue);
    }
    // 装饰Map类型变量
    private __provideMapState: ObservedPropertyAbstractPU<Map<number, string>
    // 装饰Set类型变量
    >;
    get provideMapState() {
        return this.__provideMapState.get();
    }
    set provideMapState(newValue: Map<number, string>) {
        this.__provideMapState.set(newValue);
    }
    // 装饰Set类型变量
    private __provideSetState: ObservedPropertyAbstractPU<Set<number>
    // 支持联合类型实例
    >;
    get provideSetState() {
        return this.__provideSetState.get();
    }
    set provideSetState(newValue: Set<number>) {
        this.__provideSetState.set(newValue);
    }
    // 支持联合类型实例
    private __provideUnionState: ObservedPropertyAbstractPU<number | undefined>;
    get provideUnionState() {
        return this.__provideUnionState.get();
    }
    set provideUnionState(newValue: number | undefined) {
        this.__provideUnionState.set(newValue);
    }
    initialRender() {
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Column.create();
        }, Column);
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(`@Consume provideSimpleState: ${this.provideSimpleState}`);
        }, Text);
        Text.pop();
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(`@Consume provideClassState: ${this.provideClassState.value}`);
        }, Text);
        Text.pop();
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(`@Consume provideInterfaceState: ${this.provideInterfaceState.value}`);
        }, Text);
        Text.pop();
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(`@Consume provideArrayState: ${this.provideArrayState.map(item => item.value).join(',')}`);
        }, Text);
        Text.pop();
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(`@Consume provideDateState: ${this.provideDateState}`);
        }, Text);
        Text.pop();
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(`@Consume provideMapState: ${Array.from(ObservedObject.GetRawObject(this.provideMapState))
                .map((kv: [
                number,
                string
            ]) => `${kv[0]}:${kv[1]}`)
                .join(',')}`);
        }, Text);
        Text.pop();
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(`@Consume provideSetState: ${Array.from(ObservedObject.GetRawObject(this.provideSetState)).join(',')}`);
        }, Text);
        Text.pop();
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(`@Consume provideUnionState: ${this.provideUnionState}`);
        }, Text);
        Text.pop();
        Column.pop();
    }
    rerender() {
        this.updateDirtyElements();
    }
}

@Provide

转换前后

转换成TS之后,和@State的操作类似。

  1. 将原有属性重写为getter和setter
  2. 声明一个以双下划线开头的私有属性,在getter和setter里访问私有属性的get和set方法
  3. 也会根据是简单类型还是复杂类型,生成 ObservedPropertySimplePU和ObservedPropertyObjectPU

image.png

初始化

@Provide除了生成 以双下划线开头的私有属性,还紧接着调用了 addProvidedVar

如果@Provide声明时还指定了可以重写的key。则会调用两次addProvidedVar,两个key指向同一个对象

image.png

ViewPU

首先创建了一个状态变量包装类ObsevedPropertyObjectPU的实例,然后调用addProvidedVar将这个实例传进去保存在providedVars_中

protected addProvidedVar(providedPropName: string, store: ObservedPropertyAbstractPU, allowOverride: boolean = false)

将状态变量存储到了 this.providedVars_ 这个Map中,我们在 鸿蒙ACE-ArkUI构建(二)、渲染控制和构建过程 分析过这个

image.png

providedVars_是一个Map

image.png

小结 见 框架行为 1.a.

@Consume 

转换前后

转换成TS之后,和@State的操作类似。

  1. 将原有属性重写为getter和setter
  2. 声明一个以双下划线开头的私有属性,在getter和setter里访问私有属性的get和set方法
  3. 不管是简单类型还是复杂类型,都是 ObservedPropertyAbstractPU,这个类是所有状态变量的父类 

image.png

初始化

Consume的初始化都是通过initializeConsume完成的

image.png

ViewPU

protected initializeConsume(providedPropName: string, consumeVarName: string): ObservedPropertyAbstractPU

这个方法会查找是否有对应key的provide变量。先查找自身的providedVars_ map,没有的话逐级往上查找父组件的providedVars_ map

image.png

ObservedPropertyAbstract

public createSync(factoryFunc: SynchedPropertyFactoryFunc): ObservedPropertyAbstract

可以看到createSync很简单,就是调用了一下传入的factoryFunc函数,在这里就是上面截图的factory。实际上最终返回了一个SynchedPropertyTwoWayPU的实例

image.png

小结 见 框架行为 1.

  1. @Provide实际上创建了一个ObservedPropertyAbstractPU类的实例。这个类也是@State等的状态变量的基类
  2. 然后将这个实例通过addProvideVar添加到本组件的providedVars_ map表中
  3. 子组件中的@Consume会在组件的constructor中查通过initializeConsume完成初始化,@Consume实际上最终创建了一个SynchedPropertyTwoWayPU。这个类也是@Link等的状态变量的基类
  4. initializeConsume会从自身开始逐层查找组件的providedVars_对应key的状态变量
  5. 我们可以简单的把@Provide和@Consume当成存储在Map中的@State和@Link

关联和更新UI 见鸿蒙ACE-状态分析@Link 的关联和更新UI部分

@Consume最终是创建了一个 SynchedPropertyTwoWayPU,和@Link的逻辑很相似。可以查看鸿蒙ACE-状态分析@Link 关联UI 部分

小结 见框架行为 2.  3.

总结

@Consume,@Provide建立关联的关系和@Link与 @State建立关联的方式非常类似。只不过@Link与@State是直接传递,@Consume与@Provide是逐层向上查找。

建立关联之后就和@Link与@State的几乎一样了