@Link装饰器:父子双向同步
developer.huawei.com/consumer/cn…
初始化规则图示
框架行为
@Link装饰的变量和其所属的自定义组件共享生命周期。
为了了解@Link变量初始化和更新机制,有必要先了解父组件和拥有@Link变量的子组件的关系,初始渲染和双向更新的流程(以父组件为@State为例)。
-
初始渲染:执行父组件的build()函数后将创建子组件的新实例。初始化过程如下:
- 必须指定父组件中的@State变量,用于初始化子组件的@Link变量。子组件的@Link变量值与其父组件的数据源变量保持同步(双向数据同步)。
- 父组件的@State状态变量包装类通过构造函数传给子组件,子组件的@Link包装类拿到父组件的@State的状态变量后,将当前@Link包装类this指针注册给父组件的@State变量。
-
@Link的数据源的更新:即父组件中状态变量更新,引起相关子组件的@Link的更新。处理步骤:
- 通过初始渲染的步骤可知,子组件@Link包装类把当前this指针注册给父组件。父组件@State变量变更后,会遍历更新所有依赖它的系统组件(elementid)和状态变量(比如@Link包装类) 。
- 通知@Link包装类更新后,子组件中所有依赖@Link状态变量的系统组件(elementId)都会被通知更新。以此实现父组件对子组件的状态数据同步。
-
@Link的更新:当子组件中@Link更新后,处理步骤如下(以父组件为@State为例):
- @Link更新后,调用父组件的@State包装类的set方法,将更新后的数值同步回父组件。
- 子组件@Link和父组件@State分别遍历依赖的系统组件,进行对应的UI的更新。以此实现子组件@Link同步回父组件@State。
转换前代码
ts 代码解读复制代码/**
*
* LinkChildCmpt.ets
* Created by unravel on 2024/5/3
* @abstract
*/
import { StateClass, StateInterface } from './StateDemoPage'
@Component
export struct LinkChildCmpt {
// 父组件@State到子组件@Link简单数据类型同步
@Link simpleLink: number
// class类型
@Link classLink: StateClass
// 从父组件中的@State类对象属性到@Link简单类型的同步
@Link classValueLink: string
// interface类型
@Link interfaceLink: StateInterface
// 数组类型
@Link arrayLink: StateClass[]
// 父组件@State数组项到子组件@Link简单数据类型同步
@Link array0IndexValue: string
// 从父组件中的@State数组项到@Link class类型的同步
@Link array0IndexLink: StateClass
// 日期类型
@Link dateLink: Date
// 装饰Map类型变量
@Link mapLink: Map<number, string>
// 装饰Set类型变量
@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}`)
}
}
}
转换后代码
ts 代码解读复制代码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();
}
// 父组件@State到子组件@Link简单数据类型同步
private __simpleLink: SynchedPropertySimpleTwoWayPU<number
// class类型
>;
get simpleLink() {
return this.__simpleLink.get();
}
set simpleLink(newValue: number) {
this.__simpleLink.set(newValue);
}
// class类型
private __classLink: SynchedPropertySimpleOneWayPU<StateClass
// 从父组件中的@State类对象属性到@Link简单类型的同步
>;
get classLink() {
return this.__classLink.get();
}
set classLink(newValue: StateClass) {
this.__classLink.set(newValue);
}
// 从父组件中的@State类对象属性到@Link简单类型的同步
private __classValueLink: SynchedPropertySimpleTwoWayPU<string
// interface类型
>;
get classValueLink() {
return this.__classValueLink.get();
}
set classValueLink(newValue: string) {
this.__classValueLink.set(newValue);
}
// interface类型
private __interfaceLink: SynchedPropertySimpleOneWayPU<StateInterface
// 数组类型
>;
get interfaceLink() {
return this.__interfaceLink.get();
}
set interfaceLink(newValue: StateInterface) {
this.__interfaceLink.set(newValue);
}
// 数组类型
private __arrayLink: SynchedPropertySimpleOneWayPU<StateClass[]
// 父组件@State数组项到子组件@Link简单数据类型同步
>;
get arrayLink() {
return this.__arrayLink.get();
}
set arrayLink(newValue: StateClass[]) {
this.__arrayLink.set(newValue);
}
// 父组件@State数组项到子组件@Link简单数据类型同步
private __array0IndexValue: SynchedPropertySimpleTwoWayPU<string
// 从父组件中的@State数组项到@Link class类型的同步
>;
get array0IndexValue() {
return this.__array0IndexValue.get();
}
set array0IndexValue(newValue: string) {
this.__array0IndexValue.set(newValue);
}
// 从父组件中的@State数组项到@Link class类型的同步
private __array0IndexLink: SynchedPropertySimpleOneWayPU<StateClass
// 日期类型
>;
get array0IndexLink() {
return this.__array0IndexLink.get();
}
set array0IndexLink(newValue: StateClass) {
this.__array0IndexLink.set(newValue);
}
// 日期类型
private __dateLink: SynchedPropertySimpleOneWayPU<Date
// 装饰Map类型变量
>;
get dateLink() {
return this.__dateLink.get();
}
set dateLink(newValue: Date) {
this.__dateLink.set(newValue);
}
// 装饰Map类型变量
private __mapLink: SynchedPropertySimpleOneWayPU<Map<number, string>
// 装饰Set类型变量
>;
get mapLink() {
return this.__mapLink.get();
}
set mapLink(newValue: Map<number, string>) {
this.__mapLink.set(newValue);
}
// 装饰Set类型变量
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的操作类似。
- 将原有属性重写为getter和setter
- 声明一个以双下划线开头的私有属性,在getter和setter里访问私有属性的get和set方法
初始化
-
@Link状态变量转换成TS代码之后对应两个不同的类
-
SynchedPropertySimpleTwoWayPU
由简单类型转换而来,不管这个简单类型是否是由父组件中状态变量的属性传递而来,还是基本类型传递而来。只要声明的时候是简单类型,就会生成 SynchedPropertySimpleTwoWayPU
-
SynchedPropertyObjectTwoWayPU
由非简单类型转换而来
传递
组件通过命名参数机制,将包装的@State状态对象传递过去了,即实际存储值的ObservedPropertyPU对象,在这里就是__simpleState等
小结 见 框架行为 1.b.
SynchedPropertyTwoWayPU
SynchedPropertySimpleTwoWayPU和SynchedPropertyObjectTwoWayPU 是SynchedPropertyTwoWayPU的子类
至于怎么区分的,我们可以在gitee.com/openharmony…里找到代码
判断逻辑和@State很类似,可以参考 鸿蒙ACE-状态分析@State ObservedPropertySimplePU、ObservedPropertyObjectPU 部分
结论:isBasicType里面判断,如果是string、boolean、number、enum、bigin以及他们的包装类String、Boolean、Number等都是基础类型
关联UI
SynchedPropertyTwoWayPU
SynchedPropertyTwoWayPU继承自ObservedPropertyAbstractPU,在constructor的时候将视图和属性传入了父类ObservedPropertyAbstractPU
ObservedPropertyAbstractPU
如果传入的subscriber是视图就通过owningView_持有,其他情况下放入到subscriberRefs_中
之后的逻辑就和@State关联UI很相似,可以参考 鸿蒙ACE-状态分析@State ObservedPropertyPU 怎么关联UI? 部分
结论:状态变量创建的时候会将视图this传进去,在状态变量对象内部通过owningView持有了视图实例
关联使用状态变量的UI
get方法里调用了recordPropertyDependentUpdate, 这部分可以查看 鸿蒙ACE-状态分析@State
更新UI
@Link 更改时,数据源也更改(set方法)
SynchedPropertyTwoWayPU
public set(newValue: C): void
set方法和@State的set方法很相似,参考Done-鸿蒙ACE-状态分析@State ObservedPropertyPU 怎么更新UI? 部分
同时我们看到设置状态值的时候调用了setObject
private setObject(newValue: C): void
setObject内部调用了source_.set。调用source_.set之后的流程就和@Prop很相似了,可以参考 鸿蒙ACE-状态分析@Prop source_.set 流程
setObject最终会导致本类的syncPeerHasChanged调用。由于上一步的notifyPropertyHasChangePU也会最终调用syncPeerHasChanged。所以上一步设置了一个标记位changeNotificationIsOngoing_
防止重复刷新调用
public syncPeerHasChanged(eventSource: ObservedPropertyAbstractPU): void
可以看到是有changeNotificationIsOngoing_的标记位用于防止死循环的
source_来源
SynchedPropertyTwoWayPU
constructor(source: ObservedPropertyObjectAbstract, owningChildView: IPropertySubscriber, thisPropertyName: PropertyInfo)
我们知道@Prop对应类的constructor内部会把装饰的值包装成一个 ObservedPropertyObjectAbstract
同样的,我们看下@Link里面的source_是怎么来的
小结:见框架行为 3.
数据源更改时,@Link状态变量怎么更新?
我们看下this.source_所在类的set方法。上面我们知道@Link包装对象被添加到了source的subscriber里面,我们关注下这个变量
ObservedPropertyAbstractPU
public addSubscriber(subscriber: ISinglePropertyChangeSubscriber):void
我们看到addSubscriber把subscriber添加到了两个集合中。一个是SubscriberManager map结构一个是subscriberRefs_ set结构
subscriberRefs_ 是一个Set集合
ObservedPropertyPU set
因为@Link的数据源是一个ObservedPropertyPU,所以我们可以看下这个里面的set方法
参考 鸿蒙ACE-状态分析@State set章节,我们知道最终调用到了notifyPropertyHasChangedPU、notifyTrackedObjectPropertyHasChanged
我们能够看到最终也是调用了subscriber的syncPeerHasChanged。这个subscriber就是本类,所以又回到了上面分析的 syncPeerHasChanged 章节
小结:见框架行为 1. 和 2.
总结
- @Link状态变量和@State类似,在编译后,原有变量被重写成getter和setter,同时声明一个私有的变量用于存储原有变量
- @Link内实际的存储是source_,source_指向父组件的状态变量包装类,所有的观察都是基于source_,所以@Link也只能观测一层属性的访问
- 其他的可以参见 框架行为
评论记录:
回复评论: