鸿蒙ACE-V1状态分析@Track

@Track装饰器:class对象属性级更新

developer.huawei.com/consumer/cn…

@Track是class对象的属性装饰器。当一个class对象是状态变量时,@Track装饰的属性发生变化,只会触发该属性关联的UI更新;而未被标记的属性不能在UI中使用

观察变化和行为表现

当一个class对象是状态变量时,@Track装饰的属性发生变化,该属性关联的UI触发更新。

说明:当class对象中没有一个属性被标记@Track,行为与原先保持不变。@Track没有深度观测的功能。

代码转换前


class LogTrack {
  @Track str1: string;
  @Track str2: string;

  constructor(str1: string) {
    this.str1 = str1;
    this.str2 = 'World';
  }
}

class LogNotTrack {
  str1: string;
  str2: string;

  constructor(str1: string) {
    this.str1 = str1;
    this.str2 = '世界';
  }
}

@Component
export struct TrackChildCmpt {
  @State logTrack: LogTrack = new LogTrack('Hello');
  @State logNotTrack: LogNotTrack = new LogNotTrack('你好');

  isRender(index: number) {
    console.log(`Text ${index} is rendered`);
    return 50;
  }

  build() {
    Row() {
      Column() {
        Text(this.logTrack.str1)
          .fontSize(this.isRender(1))
          .fontWeight(FontWeight.Bold)
        Text(this.logTrack.str2)
          .fontSize(this.isRender(2))
          .fontWeight(FontWeight.Bold)
        Button('change logTrack.str1')
          .onClick(() => {
            this.logTrack.str1 = 'Bye';
          })
        Text(this.logNotTrack.str1)
          .fontSize(this.isRender(3))
          .fontWeight(FontWeight.Bold)
        Text(this.logNotTrack.str2)
          .fontSize(this.isRender(4))
          .fontWeight(FontWeight.Bold)
        Button('change logNotTrack.str1')
          .onClick(() => {
            this.logNotTrack.str1 = '再见';
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

代码转换后

if (!("finalizeConstruction" in ViewPU.prototype)) {
    Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { });
}
interface TrackChildCmpt_Params {
    logTrack?: LogTrack;
    logNotTrack?: LogNotTrack;
}

class LogTrack {
    @Track
    str1: string;
    @Track
    str2: string;
    constructor(str1: string) {
        this.str1 = str1;
        this.str2 = 'World';
    }
}
class LogNotTrack {
    str1: string;
    str2: string;
    constructor(str1: string) {
        this.str1 = str1;
        this.str2 = '世界';
    }
}
export class TrackChildCmpt extends ViewPU {
    constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) {
        super(parent, __localStorage, elmtId, extraInfo);
        if (typeof paramsLambda === "function") {
            this.paramsGenerator_ = paramsLambda;
        }
        this.__logTrack = new ObservedPropertyObjectPU(new LogTrack('Hello'), this, "logTrack");
        this.__logNotTrack = new ObservedPropertyObjectPU(new LogNotTrack('你好'), this, "logNotTrack");
        this.setInitiallyProvidedValue(params);
        this.finalizeConstruction();
    }
    setInitiallyProvidedValue(params: TrackChildCmpt_Params) {
        if (params.logTrack !== undefined) {
            this.logTrack = params.logTrack;
        }
        if (params.logNotTrack !== undefined) {
            this.logNotTrack = params.logNotTrack;
        }
    }
    updateStateVars(params: TrackChildCmpt_Params) {
    }
    purgeVariableDependenciesOnElmtId(rmElmtId) {
        this.__logTrack.purgeDependencyOnElmtId(rmElmtId);
        this.__logNotTrack.purgeDependencyOnElmtId(rmElmtId);
    }
    aboutToBeDeleted() {
        this.__logTrack.aboutToBeDeleted();
        this.__logNotTrack.aboutToBeDeleted();
        SubscriberManager.Get().delete(this.id__());
        this.aboutToBeDeletedInternal();
    }
    private __logTrack: ObservedPropertyObjectPU<LogTrack>;
    get logTrack() {
        return this.__logTrack.get();
    }
    set logTrack(newValue: LogTrack) {
        this.__logTrack.set(newValue);
    }
    private __logNotTrack: ObservedPropertyObjectPU<LogNotTrack>;
    get logNotTrack() {
        return this.__logNotTrack.get();
    }
    set logNotTrack(newValue: LogNotTrack) {
        this.__logNotTrack.set(newValue);
    }
    isRender(index: number) {
        console.log(`Text ${index} is rendered`);
        return 50;
    }
    initialRender() {
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Row.create();
            Row.height('100%');
        }, Row);
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Column.create();
            Column.width('100%');
        }, Column);
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(this.logTrack.str1);
            Text.fontSize(this.isRender(1));
            Text.fontWeight(FontWeight.Bold);
        }, Text);
        Text.pop();
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(this.logTrack.str2);
            Text.fontSize(this.isRender(2));
            Text.fontWeight(FontWeight.Bold);
        }, Text);
        Text.pop();
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Button.createWithLabel('change logTrack.str1');
            Button.onClick(() => {
                this.logTrack.str1 = 'Bye';
            });
        }, Button);
        Button.pop();
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(this.logNotTrack.str1);
            Text.fontSize(this.isRender(3));
            Text.fontWeight(FontWeight.Bold);
        }, Text);
        Text.pop();
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(this.logNotTrack.str2);
            Text.fontSize(this.isRender(4));
            Text.fontWeight(FontWeight.Bold);
        }, Text);
        Text.pop();
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Button.createWithLabel('change logNotTrack.str1');
            Button.onClick(() => {
                this.logNotTrack.str1 = '再见';
            });
        }, Button);
        Button.pop();
        Column.pop();
        Row.pop();
    }
    rerender() {
        this.updateDirtyElements();
    }
}

@Track

转换前后

@Track转换成TS之后,@Track并没有被转换成其他的代码

image.png

状态变量

可以看到状态变量的修改是@State的底层转换逻辑。 @Track属性没有特殊的逻辑

image.png

转换前后的传参

使用和传参的地方,转换前后也没有区别。

image.png

Track装饰器

我们知道@Track是一个属性装饰器,在ace源码里面搜索一下这个装饰器的实现

Track

function Track(target: Object, property: string)

Track是一个属性装饰器,它给属性所属的对象实例添加了两个boolean类型的属性,并且值都为true。

我们看下这两个属性在哪里被用到了

image.png

TrackedObject

有两个方法中使用了 TrackedObject.___IS_TRACKED_OPTIMISED。我们分别看一下

public static isCompatibilityMode(obj: Object): boolean

用于判断是否是兼容模式,这包括基本类型、未使用@Track装饰属性的class类型、Object类型

  1. !obj 用于处理undefined和null。 这是因为API11之后undefined和null也可以作为状态变量了
  2. (typeof obj !== ‘object’) 用于判断普通基本类型以及他们的包装类型(boolean,number,string,Boolean,Number,String)
  3. !Reflect.has(obj, TrackedObject.___IS_TRACKED_OPTIMISED   用于判断未被@Track装饰的class和Object(Object包括所有字面量、new 创建的所有对象)

image.png

public static needsPropertyReadCb(obj: Object): boolean

用于判断是使用了@Track装饰了属性的对象

image.png

TrackedObject.___TRACKED_PREFIX

从Track装饰器的实现我们知道,使用Track之后给对象添加了一个${TrackedObject.___TRACKED_PREFIX}${property}的boolean属性

我们搜一下TrackedObject.___TRACKED_PREFIX的使用。有一处调用,位于TrackedObject的 notifyObjectValueAssignment 方法

public static notifyObjectValueAssignment(obj1: Object, obj2: Object, notifyPropertyChanged: () => void, // notify as assignment (none-optimised) notifyTrackedPropertyChange: (propName) => void, obSelf: ObservedPropertyAbstractPU): boolean

这个方法是在状态变量的set方法内调用的,可以回忆一下 鸿蒙ACE-状态分析@State 的set方法

image.png

notifyPropertyChanged

用于通知普通属性变化,对应于ObservedPropertyPU、SynchedPropertyNestedObjectPU、SynchedPropertyOneWayPU、SynchedPropertyTwoWayPU等的 notifyPropertyHasChangedPU

notifyTrackedPropertyChange

用于通知Track属性变化,对应于ObservedPropertyPU、SynchedPropertyNestedObjectPU、SynchedPropertyOneWayPU、SynchedPropertyTwoWayPU等的 notifyTrackedObjectPropertyHasChanged

关于这两个函数的实现可以查看 鸿蒙ACE-状态分析@State  notifyPropertyHasChangedPU、notifyTrackedObjectPropertyHasChanged 部分

ObservedPropertyAbstractPU

我们这次仔细看一下notifyTrackedObjectPropertyHasChanged的实现

protected notifyTrackedObjectPropertyHasChanged(changedPropertyName : string) : void

可以看到内部调用viewPropertyHasChanged和syncPeerTrackedPropertyHasChanged的时候,都是获取依赖对应属性的组件id。

我们看下怎么获取的@Track属性关联的那些组件

image.png

PropertyDependencies

public getTrackedObjectPropertyDependencies(changedObjectProperty: string, debugInfo: string): Set

依赖某个属性的所有组件存在于一个Map中。key是属性的名称,value是依赖这个属性的组件的id组成的集合

我们看到依赖的组件id是通过 addTrackedObjectPropertyDependency 添加的。看下谁调用了这个方法

类似的逻辑可以参考 鸿蒙ACE-状态分析@State

image.png

protected recordTrackObjectPropertyDependencyForElmtId(renderingElmtId : number, readTrackedPropertyName : string) : void

那么调用这个方法的地方又有哪些呢?

image.png

ObservedPropertyPU

protected onOptimisedObjectPropertyRead(readObservedObject: T, readPropertyName: string, isTracked: boolean) : void

可以看到这个方法是一个回调,方法名很容易理解,被优化的属性读取时的回调,这里被优化的属性就是@Track装饰的属性。

再看一下 onOptimisedObjectPropertyRead 回调什么时候被调用

image.png

public get(): T

image.png

ok,我们再看一下shouldInstallTrackedObjectReadCb这个标识什么时候为true

private setValueInternal(newValue: T): boolean

我们发现在setValueInternal方法里面,会根据TrackedObject.needsPropertyReadCb的返回值将shouldInstallTrackedObjectReadCb置为true或false

TrackedObject.needsPropertyReadCb这个方法我们在上面看到过了,如果有这个对象里面有@Track属性,就会置为true

同样的我们这里还需要提一嘴,在ObservedPropertyPU的constructor和set方法里都会调用setValueInternal。所以组件一创建的时候shouldInstallTrackedObjectReadCb就是true了

image.png

总结

我们总结一下Track装饰器生效的过程

  1. @Track装饰属性,编译完成后会给属性所在实例对象添加两个额外属性

    1. 一个是boolean属性TrackedObject.___IS_TRACKED_OPTIMISED,标志这个对象被Track装饰过
    2. 一个是boolean属性TrackedObject.___TRACKED_PREFIX+属性的本来名称。可以通过它来区分该属性是普通属性还是Track属性
  2. @Track装饰的类是一个状态变量时

    1. 状态变量底层转换成ObservedPropertyObjectPU,在这个对象初始化时会将shouldInstallTrackedObjectReadCb置为true
    2. 之后我们在组件中通过get获取这个属性时,会将组件id保存在一个Map中,这个Map以属性名为key,依赖这个属性的所有组件id组成的Set为value
    3. 在我们通过set方法修改属性时,在Map中找到依赖这个属性的所有的组件id,然后将这些组件标记为dirty。然后再下次Vsync过来时,这些组件就会被更新

参考资料

  1. wangdoc.com/es6/proxy
  2. gitee.com/openharmony…
  3. 声明式范式的语法编译转换,语法验证 https://gitee.com/openharmony/developtools_ace_ets2bundle
阅读全文
下载说明:
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.shuli.cc/?p=20250,转载请注明出处。
0

评论0

显示验证码
没有账号?注册  忘记密码?