鸿蒙ACE-V1状态分析@Link

@Link装饰器:父子双向同步

developer.huawei.com/consumer/cn…

初始化规则图示

image.png

框架行为

@Link装饰的变量和其所属的自定义组件共享生命周期

为了了解@Link变量初始化和更新机制,有必要先了解父组件和拥有@Link变量的子组件的关系,初始渲染和双向更新的流程(以父组件为@State为例)。

  1. 初始渲染:执行父组件的build()函数后将创建子组件的新实例。初始化过程如下:

    1. 必须指定父组件中的@State变量,用于初始化子组件的@Link变量。子组件的@Link变量值与其父组件的数据源变量保持同步(双向数据同步)。
    2. 父组件的@State状态变量包装类通过构造函数传给子组件,子组件的@Link包装类拿到父组件的@State的状态变量后,将当前@Link包装类this指针注册给父组件的@State变量。
  2. @Link的数据源的更新:即父组件中状态变量更新,引起相关子组件的@Link的更新。处理步骤:

    1. 通过初始渲染的步骤可知,子组件@Link包装类把当前this指针注册给父组件。父组件@State变量变更后,会遍历更新所有依赖它的系统组件(elementid)和状态变量(比如@Link包装类)
    2. 通知@Link包装类更新后,子组件中所有依赖@Link状态变量的系统组件(elementId)都会被通知更新。以此实现父组件对子组件的状态数据同步
  3. @Link的更新:当子组件中@Link更新后,处理步骤如下(以父组件为@State为例):

    1. @Link更新后,调用父组件的@State包装类的set方法,将更新后的数值同步回父组件
    2. 子组件@Link和父组件@State分别遍历依赖的系统组件,进行对应的UI的更新。以此实现子组件@Link同步回父组件@State

转换前代码


import { StateClass, StateInterface } from './StateDemoPage'
 
@Component
export struct LinkChildCmpt {
  
  @Link simpleLink: number
  
  @Link classLink: StateClass
  
  @Link classValueLink: string
  
  @Link interfaceLink: StateInterface
  
  @Link arrayLink: StateClass[]
  
  @Link array0IndexValue: string
  
  @Link array0IndexLink: StateClass
  
  @Link dateLink: Date
  
  @Link mapLink: Map<number, string>
  
  @Link setLink: Set<number>
  
  @Link unionLink: number | undefined
 
  build() {
    Column() {
      Text(`@Link simpleLink: ${this.simpleLink}`)
      Text(`@Link classLink: ${this.classLink.value}`)
      Text(`@Link classValueLink: ${this.classValueLink}`)
      Text(`@Link interfaceLink: ${this.interfaceLink.value}`)
      Text(`@Link arrayLink: ${this.arrayLink.map(item => item.value).join(',')}`)
      Text(`@Link array0IndexValue: ${this.array0IndexValue}`)
      Text(`@Link array0IndexLink: ${this.array0IndexLink.value}`)
      Text(`@Link dateLink: ${this.dateLink}`)
      Text(`@Link mapLink: ${Array.from(this.mapLink).map((kv: [number, string]) => `${kv[0]}:${kv[1]}`).join(',')}`)
      Text(`@Link setLink: ${Array.from(this.setLink).join(',')}`)
      Text(`@Link unionLink: ${this.unionLink}`)
    }
  }
}

转换后代码

if (!("finalizeConstruction" in ViewPU.prototype)) {
    Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { });
}
interface LinkChildCmpt_Params {
    simpleLink?: number;
    classLink?: StateClass;
    classValueLink?: string;
    interfaceLink?: StateInterface;
    arrayLink?: StateClass[];
    array0IndexValue?: string;
    array0IndexLink?: StateClass;
    dateLink?: Date;
    mapLink?: Map<number, string>;
    setLink?: Set<number>;
    unionLink?: number | undefined;
}
import type { StateClass, StateInterface } from './StateDemoPage';
export class LinkChildCmpt extends ViewPU {
    constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) {
        super(parent, __localStorage, elmtId, extraInfo);
        if (typeof paramsLambda === "function") {
            this.paramsGenerator_ = paramsLambda;
        }
        this.__simpleLink = new SynchedPropertySimpleTwoWayPU(params.simpleLink, this, "simpleLink");
        this.__classLink = new SynchedPropertyObjectTwoWayPU(params.classLink, this, "classLink");
        this.__classValueLink = new SynchedPropertySimpleTwoWayPU(params.classValueLink, this, "classValueLink");
        this.__interfaceLink = new SynchedPropertyObjectTwoWayPU(params.interfaceLink, this, "interfaceLink");
        this.__arrayLink = new SynchedPropertyObjectTwoWayPU(params.arrayLink, this, "arrayLink");
        this.__array0IndexValue = new SynchedPropertySimpleTwoWayPU(params.array0IndexValue, this, "array0IndexValue");
        this.__array0IndexLink = new SynchedPropertyObjectTwoWayPU(params.array0IndexLink, this, "array0IndexLink");
        this.__dateLink = new SynchedPropertyObjectTwoWayPU(params.dateLink, this, "dateLink");
        this.__mapLink = new SynchedPropertyObjectTwoWayPU(params.mapLink, this, "mapLink");
        this.__setLink = new SynchedPropertyObjectTwoWayPU(params.setLink, this, "setLink");
        this.__unionLink = new SynchedPropertyObjectTwoWayPU(params.unionLink, this, "unionLink");
        this.setInitiallyProvidedValue(params);
        this.finalizeConstruction();
    }
    setInitiallyProvidedValue(params: LinkChildCmpt_Params) {
    }
    updateStateVars(params: LinkChildCmpt_Params) {
    }
    purgeVariableDependenciesOnElmtId(rmElmtId) {
        this.__simpleLink.purgeDependencyOnElmtId(rmElmtId);
        this.__classLink.purgeDependencyOnElmtId(rmElmtId);
        this.__classValueLink.purgeDependencyOnElmtId(rmElmtId);
        this.__interfaceLink.purgeDependencyOnElmtId(rmElmtId);
        this.__arrayLink.purgeDependencyOnElmtId(rmElmtId);
        this.__array0IndexValue.purgeDependencyOnElmtId(rmElmtId);
        this.__array0IndexLink.purgeDependencyOnElmtId(rmElmtId);
        this.__dateLink.purgeDependencyOnElmtId(rmElmtId);
        this.__mapLink.purgeDependencyOnElmtId(rmElmtId);
        this.__setLink.purgeDependencyOnElmtId(rmElmtId);
        this.__unionLink.purgeDependencyOnElmtId(rmElmtId);
    }
    aboutToBeDeleted() {
        this.__simpleLink.aboutToBeDeleted();
        this.__classLink.aboutToBeDeleted();
        this.__classValueLink.aboutToBeDeleted();
        this.__interfaceLink.aboutToBeDeleted();
        this.__arrayLink.aboutToBeDeleted();
        this.__array0IndexValue.aboutToBeDeleted();
        this.__array0IndexLink.aboutToBeDeleted();
        this.__dateLink.aboutToBeDeleted();
        this.__mapLink.aboutToBeDeleted();
        this.__setLink.aboutToBeDeleted();
        this.__unionLink.aboutToBeDeleted();
        SubscriberManager.Get().delete(this.id__());
        this.aboutToBeDeletedInternal();
    }
    
    private __simpleLink: SynchedPropertySimpleTwoWayPU<number
    
    >;
    get simpleLink() {
        return this.__simpleLink.get();
    }
    set simpleLink(newValue: number) {
        this.__simpleLink.set(newValue);
    }
    
    private __classLink: SynchedPropertySimpleOneWayPU<StateClass
    
    >;
    get classLink() {
        return this.__classLink.get();
    }
    set classLink(newValue: StateClass) {
        this.__classLink.set(newValue);
    }
    
    private __classValueLink: SynchedPropertySimpleTwoWayPU<string
    
    >;
    get classValueLink() {
        return this.__classValueLink.get();
    }
    set classValueLink(newValue: string) {
        this.__classValueLink.set(newValue);
    }
    
    private __interfaceLink: SynchedPropertySimpleOneWayPU<StateInterface
    
    >;
    get interfaceLink() {
        return this.__interfaceLink.get();
    }
    set interfaceLink(newValue: StateInterface) {
        this.__interfaceLink.set(newValue);
    }
    
    private __arrayLink: SynchedPropertySimpleOneWayPU<StateClass[]
    
    >;
    get arrayLink() {
        return this.__arrayLink.get();
    }
    set arrayLink(newValue: StateClass[]) {
        this.__arrayLink.set(newValue);
    }
    
    private __array0IndexValue: SynchedPropertySimpleTwoWayPU<string
    
    >;
    get array0IndexValue() {
        return this.__array0IndexValue.get();
    }
    set array0IndexValue(newValue: string) {
        this.__array0IndexValue.set(newValue);
    }
    
    private __array0IndexLink: SynchedPropertySimpleOneWayPU<StateClass
    
    >;
    get array0IndexLink() {
        return this.__array0IndexLink.get();
    }
    set array0IndexLink(newValue: StateClass) {
        this.__array0IndexLink.set(newValue);
    }
    
    private __dateLink: SynchedPropertySimpleOneWayPU<Date
    
    >;
    get dateLink() {
        return this.__dateLink.get();
    }
    set dateLink(newValue: Date) {
        this.__dateLink.set(newValue);
    }
    
    private __mapLink: SynchedPropertySimpleOneWayPU<Map<number, string>
    
    >;
    get mapLink() {
        return this.__mapLink.get();
    }
    set mapLink(newValue: Map<number, string>) {
        this.__mapLink.set(newValue);
    }
    
    private __setLink: SynchedPropertySimpleOneWayPU<Set<number>
    
    >;
    get setLink() {
        return this.__setLink.get();
    }
    set setLink(newValue: Set<number>) {
        this.__setLink.set(newValue);
    }
    
    private __unionLink: SynchedPropertySimpleOneWayPU<number | undefined>;
    get unionLink() {
        return this.__unionLink.get();
    }
    set unionLink(newValue: number | undefined) {
        this.__unionLink.set(newValue);
    }
    initialRender() {
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Column.create();
        }, Column);
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(`@Link simpleLink: ${this.simpleLink}`);
        }, Text);
        Text.pop();
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(`@Link classLink: ${this.classLink.value}`);
        }, Text);
        Text.pop();
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(`@Link classValueLink: ${this.classValueLink}`);
        }, Text);
        Text.pop();
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(`@Link interfaceLink: ${this.interfaceLink.value}`);
        }, Text);
        Text.pop();
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(`@Link arrayLink: ${this.arrayLink.map(item => item.value).join(',')}`);
        }, Text);
        Text.pop();
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(`@Link array0IndexValue: ${this.array0IndexValue}`);
        }, Text);
        Text.pop();
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(`@Link array0IndexLink: ${this.array0IndexLink.value}`);
        }, Text);
        Text.pop();
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(`@Link dateLink: ${this.dateLink}`);
        }, Text);
        Text.pop();
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(`@Link mapLink: ${Array.from(ObservedObject.GetRawObject(this.mapLink)).map((kv: [
                number,
                string
            ]) => `${kv[0]}:${kv[1]}`).join(',')}`);
        }, Text);
        Text.pop();
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(`@Link setLink: ${Array.from(ObservedObject.GetRawObject(this.setLink)).join(',')}`);
        }, Text);
        Text.pop();
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(`@Link unionLink: ${this.unionLink}`);
        }, Text);
        Text.pop();
        Column.pop();
    }
    rerender() {
        this.updateDirtyElements();
    }
}

@Link

转换前后

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

  1. 将原有属性重写为getter和setter
  2. 声明一个以双下划线开头的私有属性,在getter和setter里访问私有属性的get和set方法

image.png

初始化

  1. @Link状态变量转换成TS代码之后对应两个不同的类

  2. SynchedPropertySimpleTwoWayPU

    由简单类型转换而来,不管这个简单类型是否是由父组件中状态变量的属性传递而来,还是基本类型传递而来。只要声明的时候是简单类型,就会生成 SynchedPropertySimpleTwoWayPU

  3. SynchedPropertyObjectTwoWayPU

由非简单类型转换而来

image.png

传递

组件通过命名参数机制,将包装的@State状态对象传递过去了,即实际存储值的ObservedPropertyPU对象,在这里就是__simpleState等

image.png

小结 见 框架行为 1.b.

SynchedPropertyTwoWayPU

SynchedPropertySimpleTwoWayPUSynchedPropertyObjectTwoWayPU 是SynchedPropertyTwoWayPU的子类

image.png

至于怎么区分的,我们可以在gitee.com/openharmony…里找到代码

image.png

image.png

判断逻辑和@State很类似,可以参考 鸿蒙ACE-状态分析@State  ObservedPropertySimplePU、ObservedPropertyObjectPU 部分

结论:isBasicType里面判断,如果是string、boolean、number、enum、bigin以及他们的包装类String、Boolean、Number等都是基础类型

关联UI

SynchedPropertyTwoWayPU

SynchedPropertyTwoWayPU继承自ObservedPropertyAbstractPU,在constructor的时候将视图和属性传入了父类ObservedPropertyAbstractPU

image.png

ObservedPropertyAbstractPU

如果传入的subscriber是视图就通过owningView_持有,其他情况下放入到subscriberRefs_中

image.png

之后的逻辑就和@State关联UI很相似,可以参考 鸿蒙ACE-状态分析@State  ObservedPropertyPU 怎么关联UI? 部分

结论:状态变量创建的时候会将视图this传进去,在状态变量对象内部通过owningView持有了视图实例

关联使用状态变量的UI

image.png

get方法里调用了recordPropertyDependentUpdate, 这部分可以查看 鸿蒙ACE-状态分析@State

更新UI

@Link 更改时,数据源也更改(set方法)

SynchedPropertyTwoWayPU
public set(newValue: C): void

set方法和@State的set方法很相似,参考Done-鸿蒙ACE-状态分析@State ObservedPropertyPU 怎么更新UI? 部分

同时我们看到设置状态值的时候调用了setObject

image.png

private setObject(newValue: C): void

setObject内部调用了source_.set。调用source_.set之后的流程就和@Prop很相似了,可以参考 鸿蒙ACE-状态分析@Prop source_.set 流程

image.png

setObject最终会导致本类的syncPeerHasChanged调用。由于上一步的notifyPropertyHasChangePU也会最终调用syncPeerHasChanged。所以上一步设置了一个标记位changeNotificationIsOngoing_

防止重复刷新调用

public syncPeerHasChanged(eventSource: ObservedPropertyAbstractPU): void

可以看到是有changeNotificationIsOngoing_的标记位用于防止死循环的

image.png

source_来源

SynchedPropertyTwoWayPU
constructor(source: ObservedPropertyObjectAbstract, owningChildView: IPropertySubscriber, thisPropertyName: PropertyInfo)

我们知道@Prop对应类的constructor内部会把装饰的值包装成一个  ObservedPropertyObjectAbstract

同样的,我们看下@Link里面的source_是怎么来的

image.png

小结:见框架行为 3.

数据源更改时,@Link状态变量怎么更新?

我们看下this.source_所在类的set方法。上面我们知道@Link包装对象被添加到了source的subscriber里面,我们关注下这个变量

ObservedPropertyAbstractPU
public addSubscriber(subscriber: ISinglePropertyChangeSubscriber):void

我们看到addSubscriber把subscriber添加到了两个集合中。一个是SubscriberManager map结构一个是subscriberRefs_ set结构

image.png

subscriberRefs_ 是一个Set集合

image.png

ObservedPropertyPU set

因为@Link的数据源是一个ObservedPropertyPU,所以我们可以看下这个里面的set方法

参考 鸿蒙ACE-状态分析@State set章节,我们知道最终调用到了notifyPropertyHasChangedPU、notifyTrackedObjectPropertyHasChanged

image.png

我们能够看到最终也是调用了subscriber的syncPeerHasChanged。这个subscriber就是本类,所以又回到了上面分析的 syncPeerHasChanged 章节

小结:见框架行为 1. 和 2.

总结

  1. @Link状态变量和@State类似,在编译后,原有变量被重写成getter和setter,同时声明一个私有的变量用于存储原有变量
  2. @Link内实际的存储是source_,source_指向父组件的状态变量包装类,所有的观察都是基于source_,所以@Link也只能观测一层属性的访问
  3. 其他的可以参见 框架行为

图示

image.png

参考资料

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

评论0

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