鸿蒙ACE-Repeat、ViewV2源码

247 阅读8分钟

介绍

本次代码编译基于 IDE 5.0.3.401

developer.huawei.com/consumer/cn…

Repeat循环渲染和ForEach相比有两个区别

  1. 一是优化了部分更新场景下的渲染性能
  2. 二是组件生成函数的索引index由框架侧来维护

转换前

/**
 *
 * RepeatCmp.ets
 * Created by unravel on 2024/6/14
 * @abstract
 */
@ObservedV2
class Wrap2 {
  @Trace message: string = '';

  constructor(message: string) {
    this.message = message;
  }
}

@ObservedV2
class Wrap1 {
  @Trace message: Wrap2 = new Wrap2('');

  constructor(message: string) {
    this.message = new Wrap2(message);
  }
}

@ComponentV2
export struct RepeatCmp {
  @Local simpleList: Array<Wrap1> = [new Wrap1('one'), new Wrap1('two'), new Wrap1('three')];

  build() {
    Row() {
      Column() {
        Text('点击修改第3个数组项的值')
          .fontSize(24)
          .fontColor(Color.Red)
          .onClick(() => {
            this.simpleList[2].message.message = 'new three';
          })

        Repeat<Wrap1>(this.simpleList)
          .each((obj: RepeatItem<Wrap1>) => {
            ChildItem({ item: obj.item.message.message })
              .margin({ top: 20 })
          })
          .key((item: Wrap1, index: number) => index.toString())
      }
      .justifyContent(FlexAlign.Center)
      .width('100%')
      .height('100%')
    }
    .height('100%')
    .backgroundColor(0xF1F3F5)
  }
}

@ComponentV2
struct ChildItem {
  @Require @Param item: string = '';

  build() {
    Text(this.item)
      .fontSize(30)
  }
}

转换后

if (!("finalizeConstruction" in ViewPU.prototype)) {
    Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { });
}
/**
 *
 * RepeatCmp.ets
 * Created by unravel on 2024/6/14
 * @abstract
 */
@ObservedV2
class Wrap2 {
    @Trace
    message: string = '';
    constructor(message: string) {
        this.message = message;
    }
}
@ObservedV2
class Wrap1 {
    @Trace
    message: Wrap2 = new Wrap2('');
    constructor(message: string) {
        this.message = new Wrap2(message);
    }
}
export class RepeatCmp extends ViewV2 {
    constructor(parent, params, __localStorage, elmtId = -1, paramsLambda, extraInfo) {
        super(parent, elmtId, extraInfo);
        this.simpleList = [new Wrap1('one'), new Wrap1('two'), new Wrap1('three')];
        this.finalizeConstruction();
    }
    @Local
    simpleList: Array<Wrap1>;
    initialRender() {
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Row.create();
            Row.height('100%');
            Row.backgroundColor(0xF1F3F5);
        }, Row);
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Column.create();
            Column.justifyContent(FlexAlign.Center);
            Column.width('100%');
            Column.height('100%');
        }, Column);
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create('点击修改第3个数组项的值');
            Text.fontSize(24);
            Text.fontColor(Color.Red);
            Text.onClick(() => {
                this.simpleList[2].message.message = 'new three';
            });
        }, Text);
        Text.pop();
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Repeat<Wrap1>(this.simpleList, this).each((obj: RepeatItem<Wrap1>) => {
                this.observeComponentCreation2((elmtId, isInitialRender) => {
                    __Common__.create();
                    __Common__.margin({ top: 20 });
                }, __Common__);
                {
                    this.observeComponentCreation2((elmtId, isInitialRender) => {
                        if (isInitialRender) {
                            let componentCall = new ChildItem(this, { item: obj.item.message.message }, undefined, elmtId, () => { }, { page: "entry/src/main/ets/pages/statev2/RepeatCmp.ets", line: 41 });
                            ViewV2.create(componentCall);
                            let paramsLambda = () => {
                                return {
                                    item: obj.item.message.message
                                };
                            };
                            componentCall.paramsGenerator_ = paramsLambda;
                        }
                        else {
                            this.updateStateVarsOfChildByElmtId(elmtId, {
                                item: obj.item.message.message
                            });
                        }
                    }, { name: "ChildItem" });
                }
                __Common__.pop();
            })
                .key((item: Wrap1, index: number) => index.toString()).render(isInitialRender);
        }, Repeat);
        Column.pop();
        Row.pop();
    }
    rerender() {
        this.updateDirtyElements();
    }
}
class ChildItem extends ViewV2 {
    constructor(parent, params, __localStorage, elmtId = -1, paramsLambda, extraInfo) {
        super(parent, elmtId, extraInfo);
        this.initParam("item", (params && "item" in params) ? params.item : '');
        this.finalizeConstruction();
    }
    @Param
    readonly item: string;
    initialRender() {
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(this.item);
            Text.fontSize(30);
        }, Text);
        Text.pop();
    }
    public updateStateVars(params) {
        if (params === undefined) {
            return;
        }
        if ("item" in params) {
            this.updateParam("item", params.item);
        }
    }
    rerender() {
        this.updateDirtyElements();
    }
}

Repeat

Repeat装换前后没有任何的变化,说明Repeat确实是一个单独的组件

image.png

@ObservedV2、@Trace

可以看到编译前后,这两个装饰器没有任何变化。这是因为他们两个其实是类装饰器以及属性装饰器。后面我们会继续分析它俩

image.png

@Local、ViewV2

从代码中可以看到@Local编译前后也没有任何的变化。结合之前看到的@ObservedV2、@Trace。我们大胆的推测V2版本的状态管理都是基于装饰器并且是脱离UI的

@Local装饰器的源码实现,我们后面在分析

RepeatCmp最终转换成了一个继承于ViewV2的类,同时我们也能看到每一个组件也是通过observeComponentCreation2包装起来创建的

image.png

ViewV2源码

在  https://gitee.com/openharmony/arkui_ace_engine 中我们找到了ViewV2的代码

abstract class ViewV2 extends PUV2ViewBase implements IView {

    // Set of elmtIds that need re-render
    // 需要重新渲染的组件id组成的Set集合
    protected dirtDescendantElementIds_: Set<number> = new Set<number>();

    // Set of elements for delayed update
    // 一些延迟更新的属性,包括延时更新的组件,延时更新的@Monitor监听函数,延时更新的@Computed计算函数
    private elmtIdsDelayedUpdate: Set<number> = new Set();
    private monitorIdsDelayedUpdate: Set<number> = new Set();
    private computedIdsDelayedUpdate: Set<number> = new Set();

    constructor(parent: IView, elmtId: number = UINodeRegisterProxy.notRecordingDependencies, extraInfo: ExtraInfo = undefined) {
        super(parent, elmtId, extraInfo);
        stateMgmtConsole.debug(`ViewV2 constructor: Creating @Component '${this.constructor.name}' from parent '${parent?.constructor.name}'`);
    }


    /**
     * The `freezeState` parameter determines whether this @ComponentV2 is allowed to freeze, when inactive
     * Its called with value of the `freezeWhenInactive` parameter from the @ComponentV2 decorator,
     * or it may be called with `undefined` depending on how the UI compiler works.
     *
     * @param freezeState Only the value `true` will be used to set the freeze state,
     * otherwise it inherits from its parent instance if its freezeState is true
     */
    // 每个组件的constructor方法内部最后调用的函数
    protected finalizeConstruction(freezeState?: boolean | undefined): void {
        // 遍历View中的所有Consumer装饰的变量和祖先组件的@Provider建立关联
        ProviderConsumerUtilV2.setupConsumeVarsV2(this);
        // ObserveV2是一个单例
        // 每一个被@Computed装饰的变量构建一个ComputedV2对象,存储到this的COMPUTED_RES对应的属性中
      // 每一个被@Monitor装饰的变量构建一个MonitorV2对象,存储到this的MONITOR_RES中对应的属性中
        ObserveV2.getObserve().constructComputed(this, this.constructor.name);
        ObserveV2.getObserve().constructMonitor(this, this.constructor.name);

        // Always use ID_REFS in ViewV2
        // 总是构建一个ID_REF的属性
        this[ObserveV2.ID_REFS] = {};

        // set to true if freeze parameter set for this @ComponentV2 to true
        // otherwise inherit from its parentComponent (if it exists).
        this.isCompFreezeAllowed_ = freezeState || this.isCompFreezeAllowed_;
        stateMgmtConsole.debug(`${this.debugInfo__()}: @ComponentV2 freezeWhenInactive state is set to ${this.isCompFreezeAllowed()}`);
    }

    public debugInfo__(): string {
        return `@ComponentV2 '${this.constructor.name}'[${this.id__()}]`;
    }

    // 第三个版本的View
    private get isViewV3(): boolean {
        return true;
    }

    // View被删除的时候,移除本View中的状态 会调用这个方法
    // super class will call this function from
    // its aboutToBeDeleted implementation
    protected aboutToBeDeletedInternal(): void {
        stateMgmtConsole.debug(`${this.debugInfo__()}: aboutToBeDeletedInternal`);
        // if this isDeleting_ is true already, it may be set delete status recursively by its parent, so it is not necessary
        // to set and resursively set its children any more
        if (!this.isDeleting_) {
            this.isDeleting_ = true;
        // 递归设置删除状态
            this.setDeleteStatusRecursively();
        }
        // tell UINodeRegisterProxy that all elmtIds under
        // this ViewV2 should be treated as already unregistered

        stateMgmtConsole.debug(`${this.constructor.name}: aboutToBeDeletedInternal `);

        // purge the elmtIds owned by this ViewV2 from the updateFuncByElmtId and also the state variable dependent elmtIds
        Array.from(this.updateFuncByElmtId.keys()).forEach((elmtId: number) => {
            // FIXME split View: enable delete  this purgeDeleteElmtId(elmtId);
        });

        // unregistration of ElementIDs
        stateMgmtConsole.debug(`${this.debugInfo__()}: onUnRegElementID`);

        // it will unregister removed elementids from all the ViewV2, equals purgeDeletedElmtIdsRecursively
    // 清空已删除的子组件的id,并且清除和这些组件关联的状态
        this.purgeDeletedElmtIds();

        // unregisters its own id once its children are unregistered above
        // 清除和本组件关联的状态
        UINodeRegisterProxy.unregisterRemovedElmtsFromViewPUs([this.id__()]);

        stateMgmtConsole.debug(`${this.debugInfo__()}: onUnRegElementID  - DONE`);

        /* in case ViewPU is currently frozen
           ViewPU inactiveComponents_ delete(`${this.constructor.name}[${this.id__()}]`);
        */
        MonitorV2.clearWatchesFromTarget(this);

        this.updateFuncByElmtId.clear();
        if (this.parent_) {
            this.parent_.removeChild(this);
        }
    }

    public initialRenderView(): void {
        stateMgmtProfiler.begin(`ViewV2: initialRenderView`);
        this.initialRender();
        stateMgmtProfiler.end();
    }

    public observeComponentCreation2(compilerAssignedUpdateFunc: UpdateFunc, classObject: { prototype: Object, pop?: () => void }): void {
        if (this.isDeleting_) {
            stateMgmtConsole.error(`@ComponentV2 ${this.constructor.name} elmtId ${this.id__()} is already in process of destruction, will not execute observeComponentCreation2 `);
            return;
        }
        // 获取组件名称
        const _componentName: string = (classObject && ('name' in classObject)) ? Reflect.get(classObject, 'name') as string : 'unspecified UINode';
        const _popFunc: () => void = (classObject && 'pop' in classObject) ? classObject.pop! : (): void => { };
    // 将传进来的compilerAssignedUpdateFunc进行包装
        const updateFunc = (elmtId: number, isFirstRender: boolean): void => {
            this.syncInstanceId();
            stateMgmtConsole.debug(`@ComponentV2 ${this.debugInfo__()}: ${isFirstRender ? `First render` : `Re-render/update`} ${_componentName}[${elmtId}] - start ....`);

            ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
            ObserveV2.getObserve().startRecordDependencies(this, elmtId);

            compilerAssignedUpdateFunc(elmtId, isFirstRender);
            // 非首次渲染的时候调用pop。保证除第一次之外,保证栈平衡
            if (!isFirstRender) {
                _popFunc();
            }

            let node = this.getNodeById(elmtId);
            if (node !== undefined) {
                (node as ArkComponent).cleanStageValue();
            }

            ObserveV2.getObserve().stopRecordDependencies();
            ViewStackProcessor.StopGetAccessRecording();

            stateMgmtConsole.debug(`${this.debugInfo__()}: ${isFirstRender ? `First render` : `Re-render/update`}  ${_componentName}[${elmtId}] - DONE ....`);
            this.restoreInstanceId();
        };

        const elmtId = ViewStackProcessor.AllocateNewElmetIdForNextComponent();
        // needs to move set before updateFunc.
        // make sure the key and object value exist since it will add node in attributeModifier during updateFunc.
        // 按照elmtId为key,{ updateFunc: updateFunc, classObject: classObject }为value存储到Map中
        this.updateFuncByElmtId.set(elmtId, { updateFunc: updateFunc, classObject: classObject });
        // add element id -> owning ViewV2
        // 存储组件id到组件弱引用的映射
        UINodeRegisterProxy.ElementIdToOwningViewPU_.set(elmtId, new WeakRef(this));
        try {
            updateFunc(elmtId, /* is first render */ true);
        } catch (error) {
            // avoid the incompatible change that move set function before updateFunc.
            this.updateFuncByElmtId.delete(elmtId);
            UINodeRegisterProxy.ElementIdToOwningViewPU_.delete(elmtId);
            stateMgmtConsole.applicationError(`${this.debugInfo__()} has error in update func: ${(error as Error).message}`);
            throw error;
        }
        stateMgmtConsole.debug(`${this.debugInfo__()} is initial rendering elmtId ${elmtId}, tag: ${_componentName}, and updateFuncByElmtId size :${this.updateFuncByElmtId.size}`);
    }

    /**
   *
   * @param paramVariableName
   * @param @once paramVariableName
   * @param is read only, therefore, init from parent needs to be done without
   *        causing property setter() to be called
   * @param newValue
   */
    protected initParam<Z>(paramVariableName: string, newValue: Z): void {
        VariableUtilV3.initParam<Z>(this, paramVariableName, newValue);
    }
    /**
   *
   * @param paramVariableName
   * @param @once paramVariableName
   * @param is read only, therefore, update from parent needs to be done without
   *        causing property setter() to be called
   * @param @once reject any update
    * @param newValue
   */
    protected updateParam<Z>(paramVariableName: string, newValue: Z): void {
        VariableUtilV3.updateParam<Z>(this, paramVariableName, newValue);
    }

    /**
   *  inform that UINode with given elmtId needs rerender
   *  does NOT exec @Watch function.
   *  only used on V3 code path from ObserveV2.fireChange.
   *
   * FIXME will still use in the future?
   */
    public uiNodeNeedUpdateV3(elmtId: number): void {
        if (this.isFirstRender()) {
            return;
        }

        stateMgmtProfiler.begin(`ViewV2.uiNodeNeedUpdate ${this.debugInfoElmtId(elmtId)}`);

        if(!this.isActive_) {
            this.scheduleDelayedUpdate(elmtId);
            return;
        }
        // dirtDescendantElementIds_为空的时候标记组件需要更新。
        if (!this.dirtDescendantElementIds_.size) { //  && !this runReuse_) {
            // mark ComposedElement dirty when first elmtIds are added
            // do not need to do this every time
            this.syncInstanceId();
            this.markNeedUpdate();
            this.restoreInstanceId();
        }
        this.dirtDescendantElementIds_.add(elmtId);
        stateMgmtConsole.debug(`${this.debugInfo__()}: uiNodeNeedUpdate: updated full list of elmtIds that need re-render [${this.debugInfoElmtIds(Array.from(this.dirtDescendantElementIds_))}].`);

        stateMgmtProfiler.end();
    }


    /**
 * For each recorded dirty Element in this custom component
 * run its update function
 *
 */
    public updateDirtyElements(): void {
        stateMgmtProfiler.begin('ViewV2.updateDirtyElements');
        do {
            stateMgmtConsole.debug(`${this.debugInfo__()}: updateDirtyElements (re-render): sorted dirty elmtIds: ${Array.from(this.dirtDescendantElementIds_).sort(ViewV2.compareNumber)}, starting ....`);

            // see which elmtIds are managed by this View
            // and clean up all book keeping for them
            this.purgeDeletedElmtIds();

            // process all elmtIds marked as needing update in ascending order.
            // ascending order ensures parent nodes will be updated before their children
            // prior cleanup ensure no already deleted Elements have their update func executed
            // 对需要更新的组件id进行升序排列,因为父组件的id小于子组件的id。排完序之后一定是父组件先调用,然后子组件调用
            const dirtElmtIdsFromRootNode = Array.from(this.dirtDescendantElementIds_).sort(ViewV2.compareNumber);
            // if state changed during exec update lambda inside UpdateElement, then the dirty elmtIds will be added
            // to newly created this.dirtDescendantElementIds_ Set
            // 遍历集合 调用UpdateElement更新对应的子组件
            dirtElmtIdsFromRootNode.forEach(elmtId => {
                // 更新elmtId组件
                this.UpdateElement(elmtId);
                this.dirtDescendantElementIds_.delete(elmtId);
            });

            if (this.dirtDescendantElementIds_.size) {
                stateMgmtConsole.applicationError(`${this.debugInfo__()}: New UINode objects added to update queue while re-render! - Likely caused by @Component state change during build phase, not allowed. Application error!`);
            }
        } while (this.dirtDescendantElementIds_.size);
        stateMgmtConsole.debug(`${this.debugInfo__()}: updateDirtyElements (re-render) - DONE`);
        stateMgmtProfiler.end();
    }


    public UpdateElement(elmtId: number): void {
        stateMgmtProfiler.begin('ViewV2.UpdateElement');
        if (elmtId === this.id__()) {
            // do not attempt to update itself
            stateMgmtProfiler.end();
            return;
        }
        // 根据组件id,从updateFuncByElmtId拿到对应的更新函数
        // do not process an Element that has been marked to be deleted
        const entry: UpdateFuncRecord | undefined = this.updateFuncByElmtId.get(elmtId);
        // 这个updateFunc就是observeComponentCreationV2包装的创建函数
        const updateFunc = entry ? entry.getUpdateFunc() : undefined;

        if (typeof updateFunc !== 'function') {
            stateMgmtConsole.debug(`${this.debugInfo__()}: UpdateElement: update function of elmtId ${elmtId} not found, internal error!`);
        } else {
            const componentName = entry.getComponentName();
            stateMgmtConsole.debug(`${this.debugInfo__()}: UpdateElement: re-render of ${componentName} elmtId ${elmtId} start ...`);
            stateMgmtProfiler.begin('ViewV2.updateFunc');
            // 调用更新函数
            updateFunc(elmtId, /* isFirstRender */ false);
            stateMgmtProfiler.end();
            stateMgmtProfiler.begin('ViewV2.finishUpdateFunc (native)');
            this.finishUpdateFunc(elmtId);
            stateMgmtProfiler.end();
            stateMgmtConsole.debug(`${this.debugInfo__()}: UpdateElement: re-render of ${componentName} elmtId ${elmtId} - DONE`);
        }
        stateMgmtProfiler.end();
    }

    /**
 * Retrieve child by given id
 * @param id
 * @returns child if child with this id exists and it is instance of ViewV2
 */
    public getViewV2ChildById(id: number): ViewV2 | undefined {
        // childrenWeakrefMap_ 是PUV2ViewBase中的属性,一个Map,key为组件id,value为组件的弱引用
        const childWeakRef = this.childrenWeakrefMap_.get(id);
        const child = childWeakRef ? childWeakRef.deref() : undefined;
        return (child && child instanceof ViewV2) ? child : undefined;
    }

    /**
     * findViewPUInHierarchy function needed for @Component and @ComponentV2 mixed
     * parent - child hierarchies. Not used by ViewV2
     */
    public findViewPUInHierarchy(id: number): ViewPU | undefined {
        // this ViewV2 is not a ViewPU, continue searching amongst children
        let retVal: ViewPU = undefined;
        for (const [key, value] of this.childrenWeakrefMap_.entries()) {
            retVal = value.deref().findViewPUInHierarchy(id);
            if (retVal) {
                break;
            }
        }
        return retVal;
    }

    /* Adds the elmtId to elmtIdsDelayedUpdate for delayed update
        once the view gets active
    */
    public scheduleDelayedUpdate(elmtId: number) : void {
        this.elmtIdsDelayedUpdate.add(elmtId);
    }

    // WatchIds that needs to be fired later gets added to monitorIdsDelayedUpdate
    // monitor fireChange will be triggered for all these watchIds once this view gets active
    // 添加@Monitor装饰的函数对应的monitorId
    public addDelayedMonitorIds(watchId: number) {
        stateMgmtConsole.debug(`${this.debugInfo__()} addDelayedMonitorIds called for watchId: ${watchId}`);
        this.monitorIdsDelayedUpdate.add(watchId);
    }
    // 添加@Computed装饰的函数对应的computed id
    public addDelayedComputedIds(watchId: number) {
        stateMgmtConsole.debug(`${this.debugInfo__()} addDelayedComputedIds called for watchId: ${watchId}`);
        this.computedIdsDelayedUpdate.add(watchId);
    }

    public setActiveInternal(newState: boolean): void {
        stateMgmtProfiler.begin("ViewV2.setActive");

        if (!this.isCompFreezeAllowed()) {
            stateMgmtConsole.debug(`${this.debugInfo__()}: ViewV2.setActive. Component freeze state is ${this.isCompFreezeAllowed()} - ignoring`);
            stateMgmtProfiler.end();
            return;
        }

        stateMgmtConsole.debug(`${this.debugInfo__()}: ViewV2.setActive ${newState ? ' inActive -> active' : 'active -> inActive'}`);
        this.isActive_ = newState;
        if (this.isActive_) {
          this.onActiveInternal()
        } else {
          this.onInactiveInternal();
        }
        stateMgmtProfiler.end();
    }

    private onActiveInternal(): void {
        if (!this.isActive_) {
          return;
        }

        stateMgmtConsole.debug(`${this.debugInfo__()}: onActiveInternal`);
        this.performDelayedUpdate();

        // Set 'isActive_' state for all descendant child Views
        for (const child of this.childrenWeakrefMap_.values()) {
          const childView: IView | undefined = child.deref();
          if (childView) {
            childView.setActiveInternal(this.isActive_);
          }
        }
    }

    private onInactiveInternal(): void {
        if (this.isActive_) {
          return;
        }
        stateMgmtConsole.debug(`${this.debugInfo__()}: onInactiveInternal`);

        // Set 'isActive_' state for all descendant child Views
        for (const child of this.childrenWeakrefMap_.values()) {
          const childView: IView | undefined = child.deref();
          if (childView) {
            childView.setActiveInternal(this.isActive_);
          }
        }
    }

    private performDelayedUpdate(): void {
        stateMgmtProfiler.begin("ViewV2: performDelayedUpdate");
        // 先更新@Computed装饰的计算函数,因为它可能会更改状态变量
        if(this.computedIdsDelayedUpdate.size) {
            // exec computed functions
            ObserveV2.getObserve().updateDirtyComputedProps([...this.computedIdsDelayedUpdate]);
        }
        // 然后更新@Monitor装饰的监听函数,因为在监听回调里面也可能更改状态变量
        if(this.monitorIdsDelayedUpdate.size) {
          // exec monitor functions
          ObserveV2.getObserve().updateDirtyMonitors(this.monitorIdsDelayedUpdate);
        }
        // 最后更新  之前需要更新的组件id+上面两步导致的状态变化又需要更新的组件id
        if(this.elmtIdsDelayedUpdate.size) {
          // update re-render of updated element ids once the view gets active
          if(this.dirtDescendantElementIds_.size === 0) {
            this.dirtDescendantElementIds_ = new Set(this.elmtIdsDelayedUpdate);
          }
          else {
            this.elmtIdsDelayedUpdate.forEach((element) => {
              this.dirtDescendantElementIds_.add(element);
            });
          }
        }
        this.markNeedUpdate();
        this.elmtIdsDelayedUpdate.clear();
        this.monitorIdsDelayedUpdate.clear();
        stateMgmtProfiler.end();
    }

    /*
      findProvidePU finds @Provided property recursively by traversing ViewPU's towards that of the UI tree root @Component:
      if 'this' ViewPU has a @Provide('providedPropName') return it, otherwise ask from its parent ViewPU.
      function needed for mixed @Component and @ComponentV2 parent child hierarchies.
    */
    // 从父组件开始递归的查找 providePU
    public findProvidePU(providedPropName: string): ObservedPropertyAbstractPU<any> | undefined {
        return this.getParent()?.findProvidePU(providedPropName);
    }
    // 递归从父组件初始化LocalStorage,如果没有 会初始化一个new LocalStorage
    get localStorage_(): LocalStorage {
        // FIXME check this also works for root @ComponentV2
        return (this.getParent()) ? this.getParent().localStorage_ : new LocalStorage({ /* empty */ });
    }


    public debugInfoDirtDescendantElementIdsInternal(depth: number = 0, recursive: boolean = false, counter: ProfileRecursionCounter): string {
        let retVaL: string = `\n${'  '.repeat(depth)}|--${this.constructor.name}[${this.id__()}]: {`;
        retVaL += `ViewV2 keeps no info about dirty elmtIds`;
        if (recursive) {
            this.childrenWeakrefMap_.forEach((value, key, map) => {
                retVaL += value.deref()?.debugInfoDirtDescendantElementIdsInternal(depth + 1, recursive, counter);
            });
        }

        if (recursive && depth === 0) {
            retVaL += `\nTotal: ${counter.total}`;
        }
        return retVaL;
    }


    protected debugInfoStateVars(): string {
        return ''; // TODO DFX, read out META
    }

    /**
   * on first render create a new Instance of Repeat
   * on re-render connect to existing instance
   * @param arr
   * @returns
   */
    // Repeat组件实际的创建动作
    public __mkRepeatAPI: <I>(arr: Array<I>) => RepeatAPI<I> = <I>(arr: Array<I>): RepeatAPI<I> => {
        // factory is for future extensions, currently always return the same
        const elmtId = this.getCurrentlyRenderedElmtId();
        let repeat = this.elmtId2Repeat_.get(elmtId) as __RepeatV2<I>;
        if (!repeat) {
            repeat = new __RepeatV2<I>(arr);
            this.elmtId2Repeat_.set(elmtId, repeat);
        } else {
            repeat.updateArr(arr);
        }
        return repeat;
    };
}

小结

创建相关
  1. observeComponentCreation2 负责观测实际的组件创建过程,可以参考分析 鸿蒙ACE-ArkUI构建(二)、渲染控制和构建过程

  2. observeComponentCreation2 将创建函数的原有闭包封装成updateFunc函数,并存入updateFuncByElmtId 中

    • this.updateFuncByElmtId.set(elmtId, { updateFunc: updateFunc, classObject: classObject });
    • updateFuncByElmtId是一个class,内部实际维护的是一个Map。存储updateFunc函数的时候,组件id为key,updateFunc为value
  3. observeComponentCreation2函数还会存储组件id到组件实例弱引用的映射,也是存到一个Map中

    • UINodeRegisterProxy.ElementIdToOwningViewPU_.set(elmtId, new WeakRef(this));
    • 组件id为key,组件的弱引用为value
状态变量相关
  1. ProviderConsumerUtilV2负责 建立Provider和Consumer的关系,并且定义状态变量的getter和setter

    • ProviderConsumerUtilV2.setupConsumeVarsV2(this);
    • @Consumer、@Provider装饰属性过后,原有实例上会增加一个V2_DECO_META对象,对象上会针对每一个Provider/@Consumer再生成新的属性用于标识是@Provider、@Consumer属性
  2. V2版本的状态变量管理都是通过一个单例ObserveV2进行的。单例方法为 ObserveV2.getObserve()

    1. ObserveV2.getObserve().constructComputed(this, this.constructor.name);

      • View内每一个@Computed装饰的计算属性,最终都会生成一个ComputedV2对象,这个对象持有计算属性所属的类,属性名,计算属性执行体

      • View内所有的@Computed装饰的计算属性,存放于const computedProp = Symbol.for(ComputedV2.COMPUTED_PREFIX + owningObjectName)属性中

        • computedProp属性的值是一个对象,这个对象里的每个属性就是上面对应的ComputedV2对象
    2. ObserveV2.getObserve().constructMonitor(this, this.constructor.name);

      • View内每一个@Monitor装饰的监听函数,最终都会生成一个MonitorV2对象,这个对象持有监听函数所属的类,属性名,监听函数执行体

      • View内所有的@Monitor装饰的监听函数,存放于let watchProp = Symbol.for(MonitorV2.WATCH_PREFIX + owningObjectName);属性中

        • watchProp属性的值是一个对象,这个对象里的每个属性就是上面对应的MonitorV2对象
    3. this[ObserveV2.ID_REFS] = {};

      • 这个组件内使用状态变量的组件id
更新相关

// 需要重新渲染的组件id组成的Set集合

protected dirtDescendantElementIds_: Set = new Set();

// 一些延迟更新的属性,包括延时更新的组件,延时更新的@Monitor监听函数,延时更新的@Computed计算函数

private elmtIdsDelayedUpdate: Set = new Set();

private monitorIdsDelayedUpdate: Set = new Set();

private computedIdsDelayedUpdate: Set = new Set();

参考资料

  1. 声明式范式的语法编译转换,语法验证等 https://gitee.com/openharmony/developtools_ace_ets2bundle
  2. protobuf https://github.com/protocolbuffers/protobuf
  3. ArkUI引擎 https://gitee.com/openharmony/arkui_ace_engine