深入理解 ArkUI - 页面更新原理
页面更新原理
前文已经分析对于每一个页面组件树的生成,概括起来如下:
- 每个页面基于PagePattern生成PageNode
- Index.ets中struct Index作为自定义组件生成JsView
- PageNode挂载在Ability对应的StageNode上,并标脏向RS请求Vsync信号
- 页面根节点根据build函数的描述生成对应Pattern的FrameNode挂载在,并挂载再根节点
最小化更新原理
- 页面首次创建时基于Index.ets的ui描述为每一个组件生成唯一标识id
- 状态变量发生变化会标记持有的自定义组件为脏节点并保存再渲染管线中
- 接收到Vsync信号,会更新渲染管线中的保存的脏节点
- 自定义组件的更新是通过前端框架识别build函数内部子节点的更新条件,重新执行变更的状态变量关联的子节点的UI描述
- 子节点设置新的属性或者状态变量时会与老节点比较并进行脏区标记,如果是If分支,则会动态创建或删除子节点
- 布局任务大多依赖父子关系,子节点标脏通常会标脏父节点,并保存在渲染管线中
- 渲染管线的Measure和Layout阶段重新布局,并保存大小位置等信息到RSNode
- 最后渲染管线发送绘制指令给RS进程进行绘制和显示
依赖收集
状态管理的代码在foundation/arkui/ace_engine/frameworks/bridge/declarative_frontend/engine/stateMgmt.js文件中,依赖的收集是在initialRender函数中完成,initialRender执行过程参考页面执行,initialRender会挨个执行observeComponentCreation函数
observeComponentCreation2(compilerAssignedUpdateFunc, classObject) {
const _componentName = (classObject && ("name" in classObject)) ? Reflect.get(classObject, "name") : "unspecified UINode";
const _popFunc = (classObject && "pop" in classObject) ? classObject.pop : () => { };
const updateFunc = (elmtId, isFirstRender) => {
this.syncInstanceId();
ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
this.currentlyRenderedElmtIdStack_.push(elmtId);
compilerAssignedUpdateFunc(elmtId, isFirstRender);
if (!isFirstRender) {
_popFunc();
}
this.currentlyRenderedElmtIdStack_.pop();
ViewStackProcessor.StopGetAccessRecording();
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.
this.updateFuncByElmtId.set(elmtId, { updateFunc: updateFunc, classObject: classObject });
// add element id -> owning ViewPU
UINodeRegisterProxy.ElementIdToOwningViewPU_.set(elmtId, new WeakRef(this));
try {
updateFunc(elmtId, /* is first render */ true);
}
catch (error) {
...
}
}
- 先调用ViewStackProcessor.AllocateNewElmetIdForNextComponent()生成闭包函数的elmtId
- 保存elmtId和updateFunc闭包函数到updateFuncByElmtId中
- elmtId作为参数执行闭包函数
- 如果闭包函数涉及状态变量则通过get函数保存变量名和elmtId到dependentElmtIdsByProperty_中
class My extends ViewPU {
constructor(parent, params, __localStorage, elmtId = -1) {
super(parent, __localStorage, elmtId);
this.__isShow = new ObservedPropertySimplePU(true, this, "isShow");
this.setInitiallyProvidedValue(params);
}
...
}
class ObservedPropertySimplePU extends ObservedPropertyPU {
}
class ObservedPropertyPU extends ObservedPropertyAbstractPU {
...
get() {
this.recordPropertyDependentUpdate();
if (this.shouldInstallTrackedObjectReadCb) {
ObservedObject.registerPropertyReadCb(this.wrappedValue_, this.onOptimisedObjectPropertyRead.bind(this));
} else {
}
return this.wrappedValue_;
}
}
recordPropertyDependentUpdate() {
const elmtId = this.getRenderingElmtId();
if (elmtId < 0) {
// not access recording
return;
}
/* 保存变量名和elmtId到dependentElmtIdsByProperty_中 */
this.dependentElmtIdsByProperty_.addPropertyDependency(elmtId);
}
状态变更
状态的变更在set函数中
set(newValue) {
if (this.wrappedValue_ === newValue) {
return;
}
const oldValue = this.wrappedValue_;
if (this.setValueInternal(newValue)) {
TrackedObject.notifyObjectValueAssignment(/* old value */ oldValue, /* new value */ this.wrappedValue_, this.notifyPropertyHasChangedPU.bind(this), this.notifyTrackedObjectPropertyHasChanged.bind(this));
}
}
notifyPropertyHasChangedPU() {
if (this.owningView_) {
if (this.delayedNotification_ == ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.do_not_delay) {
// send viewPropertyHasChanged right away
this.owningView_.viewPropertyHasChanged(this.info_, this.dependentElmtIdsByProperty_.getAllPropertyDependencies());
} else {
// mark this @StorageLink/Prop or @LocalStorageLink/Prop variable has having changed and notification of viewPropertyHasChanged delivery pending
this.delayedNotification_ = ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.delay_notification_pending;
}
}
this.subscriberRefs_.forEach((subscriber) => {
if (subscriber) {
if ('syncPeerHasChanged' in subscriber) {
subscriber.syncPeerHasChanged(this);
} else {
stateMgmtConsole.warn(`${this.debugInfo()}: notifyPropertyHasChangedPU: unknown subscriber ID 'subscribedId' error!`);
}
}
});
}
viewPropertyHasChanged(varName, dependentElmtIds) {
stateMgmtTrace.scopedTrace(() => {
this.syncInstanceId();
if (dependentElmtIds.size && !this.isFirstRender()) {
if (!this.dirtDescendantElementIds_.size && !this.runReuse_) {
// mark ComposedElement dirty when first elmtIds are added
// do not need to do this every time
/* 标记脏节点 */
this.markNeedUpdate();
}
for (const elmtId of dependentElmtIds) {
if (this.hasRecycleManager()) {
this.dirtDescendantElementIds_.add(this.recycleManager_.proxyNodeId(elmtId));
} else {
this.dirtDescendantElementIds_.add(elmtId);
}
}
} else {
}
/* 执行watch回调函数 */
let cb = this.watchedProps.get(varName);
if (cb) {
cb.call(this, varName);
}
this.restoreInstanceId();
}, "ViewPU.viewPropertyHasChanged", this.constructor.name, varName, dependentElmtIds.size);
}
本文暂时没有评论,来添加一个吧(●'◡'●)