介绍
本次代码编译基于 IDE 5.0.3.401
developer.huawei.com/consumer/cn…
Repeat循环渲染和ForEach相比有两个区别
- 一是优化了部分更新场景下的渲染性能
- 二是组件生成函数的索引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确实是一个单独的组件
@ObservedV2、@Trace
可以看到编译前后,这两个装饰器没有任何变化。这是因为他们两个其实是类装饰器以及属性装饰器。后面我们会继续分析它俩
@Local、ViewV2
从代码中可以看到@Local编译前后也没有任何的变化。结合之前看到的@ObservedV2、@Trace。我们大胆的推测V2版本的状态管理都是基于装饰器并且是脱离UI的
@Local装饰器的源码实现,我们后面在分析
RepeatCmp最终转换成了一个继承于ViewV2的类,同时我们也能看到每一个组件也是通过observeComponentCreation2包装起来创建的
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;
};
}
小结
创建相关
-
observeComponentCreation2 负责观测实际的组件创建过程,可以参考分析 鸿蒙ACE-ArkUI构建(二)、渲染控制和构建过程
-
observeComponentCreation2 将创建函数的原有闭包封装成updateFunc函数,并存入updateFuncByElmtId 中
- this.updateFuncByElmtId.set(elmtId, { updateFunc: updateFunc, classObject: classObject });
- updateFuncByElmtId是一个class,内部实际维护的是一个Map。存储updateFunc函数的时候,组件id为key,updateFunc为value
-
observeComponentCreation2函数还会存储组件id到组件实例弱引用的映射,也是存到一个Map中
- UINodeRegisterProxy.ElementIdToOwningViewPU_.set(elmtId, new WeakRef(this));
- 组件id为key,组件的弱引用为value
状态变量相关
-
ProviderConsumerUtilV2负责 建立Provider和Consumer的关系,并且定义状态变量的getter和setter
- ProviderConsumerUtilV2.setupConsumeVarsV2(this);
- @Consumer、@Provider装饰属性过后,原有实例上会增加一个V2_DECO_META对象,对象上会针对每一个Provider/@Consumer再生成新的属性用于标识是@Provider、@Consumer属性
-
V2版本的状态变量管理都是通过一个单例ObserveV2进行的。单例方法为 ObserveV2.getObserve()
-
ObserveV2.getObserve().constructComputed(this, this.constructor.name);
-
View内每一个@Computed装饰的计算属性,最终都会生成一个ComputedV2对象,这个对象持有计算属性所属的类,属性名,计算属性执行体
-
View内所有的@Computed装饰的计算属性,存放于const computedProp = Symbol.for(ComputedV2.COMPUTED_PREFIX + owningObjectName)属性中
- computedProp属性的值是一个对象,这个对象里的每个属性就是上面对应的ComputedV2对象
-
-
ObserveV2.getObserve().constructMonitor(this, this.constructor.name);
-
View内每一个@Monitor装饰的监听函数,最终都会生成一个MonitorV2对象,这个对象持有监听函数所属的类,属性名,监听函数执行体
-
View内所有的@Monitor装饰的监听函数,存放于let watchProp = Symbol.for(MonitorV2.WATCH_PREFIX + owningObjectName);属性中
- watchProp属性的值是一个对象,这个对象里的每个属性就是上面对应的MonitorV2对象
-
-
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();
参考资料
- 声明式范式的语法编译转换,语法验证等 https://gitee.com/openharmony/developtools_ace_ets2bundle
- protobuf https://github.com/protocolbuffers/protobuf
- ArkUI引擎 https://gitee.com/openharmony/arkui_ace_engine