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