@Prop装饰器:父子单向同步
developer.huawei.com/consumer/cn...
初始化规则图示
框架行为
要理解@Prop变量值初始化和更新机制,有必要了解父组件和拥有@Prop变量的子组件初始渲染和更新流程。
-
初始渲染:
- 执行父组件的build()函数将创建子组件的新实例,将数据源传递给子组件;
- 初始化子组件@Prop装饰的变量。
-
更新:
- 子组件@Prop更新时,更新仅停留在当前子组件,不会同步回父组件;
- 当父组件的数据源更新时,子组件的@Prop装饰的变量将被来自父组件的数据源重置,所有@Prop装饰的本地的修改将被父组件的更新覆盖。
说明
@Prop装饰的数据更新依赖其所属自定义组件的重新渲染,所以在应用进入后台后,@Prop无法刷新,推荐使用@Link代替。
转换前代码
ts
/**
*
* PropDemoCmpt.ets
* Created by unravel on 2024/5/3
* @abstract
*/
import { StateClass, StateInterface } from './StateDemoPage';
@Component
export struct PropChildCmpt {
// 本地初始化不和父组件同步
@Prop mineSimpleProp: number = 0;
// 父组件@State到子组件@Prop简单数据类型同步
@Prop simpleProp: number = 0;
// class类型
@Prop classProp: StateClass = new StateClass('class类型');
// 从父组件中的@State类对象属性到@Prop简单类型的同步
@Prop classValueProp: string = '对象属性'
// interface类型
@Prop interfaceProp: StateInterface = { value: 'interface类型' };
// 数组类型
@Prop arrayProp: StateClass[] = [new StateClass('数组项1'), new StateClass('数组项2')];
// 父组件@State数组项到子组件@Prop简单数据类型同步
@Prop array0IndexValue: string = '父组件@State数组项到子组件@Prop简单数据类型同步'
// 从父组件中的@State数组项到@Prop class类型的同步
@Prop array0IndexProp: StateClass = new StateClass('数组项0');
// 日期类型
@Prop dateProp: Date = new Date('2021-08-08')
// 装饰Map类型变量
@Prop mapProp: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]])
// 装饰Set类型变量
@Prop setProp: Set<number> = new Set([0, 1, 2, 3, 4])
// 支持联合类型实例
@Prop unionProp: number | undefined = 0;
build() {
Column() {
Text(`@Prop mineSimpleProp: ${this.mineSimpleProp}`)
Text(`@Prop simpleProp: ${this.simpleProp}`)
Text(`@Prop classProp: ${this.classProp.value}`)
Text(`@Prop classValueProp: ${this.classValueProp}`)
Text(`@Prop interfaceProp: ${this.interfaceProp.value}`)
Text(`@Prop arrayProp: ${this.arrayProp.map(item => item.value).join(',')}`)
Text(`@Prop array0IndexValue: ${this.array0IndexValue}`)
Text(`@Prop array0IndexProp: ${this.array0IndexProp.value}`)
Text(`@Prop dateProp: ${this.dateProp}`)
Text(`@Prop mapProp: ${Array.from(this.mapProp).map((kv: [number, string]) => `${kv[0]}:${kv[1]}`).join(',')}`)
Text(`@Prop setProp: ${Array.from(this.setProp).join(',')}`)
Text(`@Prop unionProp: ${this.unionProp}`)
}
}
}
转换后代码
ts
if (!("finalizeConstruction" in ViewPU.prototype)) {
Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { });
}
interface PropChildCmpt_Params {
mineSimpleProp?: number;
simpleProp?: number;
classProp?: StateClass;
classValueProp?: string;
interfaceProp?: StateInterface;
arrayProp?: StateClass[];
array0IndexValue?: string;
array0IndexProp?: StateClass;
dateProp?: Date;
mapProp?: Map<number, string>;
setProp?: Set<number>;
unionProp?: number | undefined;
}
import { StateClass } from "@bundle:com.unravel.myapplication/entry/ets/pages/StateDemoPage";
import type { StateInterface } from "@bundle:com.unravel.myapplication/entry/ets/pages/StateDemoPage";
export class PropChildCmpt extends ViewPU {
constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) {
super(parent, __localStorage, elmtId, extraInfo);
if (typeof paramsLambda === "function") {
this.paramsGenerator_ = paramsLambda;
}
this.__mineSimpleProp = new SynchedPropertySimpleOneWayPU(params.mineSimpleProp, this, "mineSimpleProp");
this.__simpleProp = new SynchedPropertySimpleOneWayPU(params.simpleProp, this, "simpleProp");
this.__classProp = new SynchedPropertyObjectOneWayPU(params.classProp, this, "classProp");
this.__classValueProp = new SynchedPropertySimpleOneWayPU(params.classValueProp, this, "classValueProp");
this.__interfaceProp = new SynchedPropertyObjectOneWayPU(params.interfaceProp, this, "interfaceProp");
this.__arrayProp = new SynchedPropertyObjectOneWayPU(params.arrayProp, this, "arrayProp");
this.__array0IndexValue = new SynchedPropertySimpleOneWayPU(params.array0IndexValue, this, "array0IndexValue");
this.__array0IndexProp = new SynchedPropertyObjectOneWayPU(params.array0IndexProp, this, "array0IndexProp");
this.__dateProp = new SynchedPropertyObjectOneWayPU(params.dateProp, this, "dateProp");
this.__mapProp = new SynchedPropertyObjectOneWayPU(params.mapProp, this, "mapProp");
this.__setProp = new SynchedPropertyObjectOneWayPU(params.setProp, this, "setProp");
this.__unionProp = new SynchedPropertyObjectOneWayPU(params.unionProp, this, "unionProp");
this.setInitiallyProvidedValue(params);
this.finalizeConstruction();
}
setInitiallyProvidedValue(params: PropChildCmpt_Params) {
if (params.mineSimpleProp === undefined) {
this.__mineSimpleProp.set(0);
}
if (params.simpleProp === undefined) {
this.__simpleProp.set(0);
}
if (params.classProp === undefined) {
this.__classProp.set(new StateClass('class类型'));
}
if (params.classValueProp === undefined) {
this.__classValueProp.set('对象属性'
// interface类型
);
}
if (params.interfaceProp === undefined) {
this.__interfaceProp.set({ value: 'interface类型' });
}
if (params.arrayProp === undefined) {
this.__arrayProp.set([new StateClass('数组项1'), new StateClass('数组项2')]);
}
if (params.array0IndexValue === undefined) {
this.__array0IndexValue.set('父组件@State数组项到子组件@Prop简单数据类型同步'
// 从父组件中的@State数组项到@Prop class类型的同步
);
}
if (params.array0IndexProp === undefined) {
this.__array0IndexProp.set(new StateClass('数组项0'));
}
if (params.dateProp === undefined) {
this.__dateProp.set(new Date('2021-08-08')
// 装饰Map类型变量
);
}
if (params.mapProp === undefined) {
this.__mapProp.set(new Map([[0, "a"], [1, "b"], [3, "c"]])
// 装饰Set类型变量
);
}
if (params.setProp === undefined) {
this.__setProp.set(new Set([0, 1, 2, 3, 4])
// 支持联合类型实例
);
}
if (params.unionProp === undefined) {
this.__unionProp.set(0);
}
}
updateStateVars(params: PropChildCmpt_Params) {
this.__mineSimpleProp.reset(params.mineSimpleProp);
this.__simpleProp.reset(params.simpleProp);
this.__classProp.reset(params.classProp);
this.__classValueProp.reset(params.classValueProp);
this.__interfaceProp.reset(params.interfaceProp);
this.__arrayProp.reset(params.arrayProp);
this.__array0IndexValue.reset(params.array0IndexValue);
this.__array0IndexProp.reset(params.array0IndexProp);
this.__dateProp.reset(params.dateProp);
this.__mapProp.reset(params.mapProp);
this.__setProp.reset(params.setProp);
this.__unionProp.reset(params.unionProp);
}
purgeVariableDependenciesOnElmtId(rmElmtId) {
this.__mineSimpleProp.purgeDependencyOnElmtId(rmElmtId);
this.__simpleProp.purgeDependencyOnElmtId(rmElmtId);
this.__classProp.purgeDependencyOnElmtId(rmElmtId);
this.__classValueProp.purgeDependencyOnElmtId(rmElmtId);
this.__interfaceProp.purgeDependencyOnElmtId(rmElmtId);
this.__arrayProp.purgeDependencyOnElmtId(rmElmtId);
this.__array0IndexValue.purgeDependencyOnElmtId(rmElmtId);
this.__array0IndexProp.purgeDependencyOnElmtId(rmElmtId);
this.__dateProp.purgeDependencyOnElmtId(rmElmtId);
this.__mapProp.purgeDependencyOnElmtId(rmElmtId);
this.__setProp.purgeDependencyOnElmtId(rmElmtId);
this.__unionProp.purgeDependencyOnElmtId(rmElmtId);
}
aboutToBeDeleted() {
this.__mineSimpleProp.aboutToBeDeleted();
this.__simpleProp.aboutToBeDeleted();
this.__classProp.aboutToBeDeleted();
this.__classValueProp.aboutToBeDeleted();
this.__interfaceProp.aboutToBeDeleted();
this.__arrayProp.aboutToBeDeleted();
this.__array0IndexValue.aboutToBeDeleted();
this.__array0IndexProp.aboutToBeDeleted();
this.__dateProp.aboutToBeDeleted();
this.__mapProp.aboutToBeDeleted();
this.__setProp.aboutToBeDeleted();
this.__unionProp.aboutToBeDeleted();
SubscriberManager.Get().delete(this.id__());
this.aboutToBeDeletedInternal();
}
// 本地初始化不和父组件同步
private __mineSimpleProp: SynchedPropertySimpleOneWayPU<number>;
get mineSimpleProp() {
return this.__mineSimpleProp.get();
}
set mineSimpleProp(newValue: number) {
this.__mineSimpleProp.set(newValue);
}
// 父组件@State到子组件@Prop简单数据类型同步
private __simpleProp: SynchedPropertySimpleOneWayPU<number>;
get simpleProp() {
return this.__simpleProp.get();
}
set simpleProp(newValue: number) {
this.__simpleProp.set(newValue);
}
// class类型
private __classProp: SynchedPropertySimpleOneWayPU<StateClass>;
get classProp() {
return this.__classProp.get();
}
set classProp(newValue: StateClass) {
this.__classProp.set(newValue);
}
// 从父组件中的@State类对象属性到@Prop简单类型的同步
private __classValueProp: SynchedPropertySimpleOneWayPU<string>;
get classValueProp() {
return this.__classValueProp.get();
}
set classValueProp(newValue: string) {
this.__classValueProp.set(newValue);
}
// interface类型
private __interfaceProp: SynchedPropertySimpleOneWayPU<StateInterface>;
get interfaceProp() {
return this.__interfaceProp.get();
}
set interfaceProp(newValue: StateInterface) {
this.__interfaceProp.set(newValue);
}
// 数组类型
private __arrayProp: SynchedPropertySimpleOneWayPU<StateClass[]>;
get arrayProp() {
return this.__arrayProp.get();
}
set arrayProp(newValue: StateClass[]) {
this.__arrayProp.set(newValue);
}
// 父组件@State数组项到子组件@Prop简单数据类型同步
private __array0IndexValue: SynchedPropertySimpleOneWayPU<string>;
get array0IndexValue() {
return this.__array0IndexValue.get();
}
set array0IndexValue(newValue: string) {
this.__array0IndexValue.set(newValue);
}
// 从父组件中的@State数组项到@Prop class类型的同步
private __array0IndexProp: SynchedPropertySimpleOneWayPU<StateClass>;
get array0IndexProp() {
return this.__array0IndexProp.get();
}
set array0IndexProp(newValue: StateClass) {
this.__array0IndexProp.set(newValue);
}
// 日期类型
private __dateProp: SynchedPropertySimpleOneWayPU<Date>;
get dateProp() {
return this.__dateProp.get();
}
set dateProp(newValue: Date) {
this.__dateProp.set(newValue);
}
// 装饰Map类型变量
private __mapProp: SynchedPropertySimpleOneWayPU<Map<number, string>>;
get mapProp() {
return this.__mapProp.get();
}
set mapProp(newValue: Map<number, string>) {
this.__mapProp.set(newValue);
}
// 装饰Set类型变量
private __setProp: SynchedPropertySimpleOneWayPU<Set<number>>;
get setProp() {
return this.__setProp.get();
}
set setProp(newValue: Set<number>) {
this.__setProp.set(newValue);
}
// 支持联合类型实例
private __unionProp: SynchedPropertySimpleOneWayPU<number | undefined>;
get unionProp() {
return this.__unionProp.get();
}
set unionProp(newValue: number | undefined) {
this.__unionProp.set(newValue);
}
initialRender() {
this.observeComponentCreation2((elmtId, isInitialRender) => {
Column.create();
}, Column);
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create(`@Prop mineSimpleProp: ${this.mineSimpleProp}`);
}, Text);
Text.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create(`@Prop simpleProp: ${this.simpleProp}`);
}, Text);
Text.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create(`@Prop classProp: ${this.classProp.value}`);
}, Text);
Text.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create(`@Prop classValueProp: ${this.classValueProp}`);
}, Text);
Text.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create(`@Prop interfaceProp: ${this.interfaceProp.value}`);
}, Text);
Text.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create(`@Prop arrayProp: ${this.arrayProp.map(item => item.value).join(',')}`);
}, Text);
Text.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create(`@Prop array0IndexValue: ${this.array0IndexValue}`);
}, Text);
Text.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create(`@Prop array0IndexProp: ${this.array0IndexProp.value}`);
}, Text);
Text.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create(`@Prop dateProp: ${this.dateProp}`);
}, Text);
Text.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create(`@Prop mapProp: ${Array.from(ObservedObject.GetRawObject(this.mapProp)).map((kv: [
number,
string
]) => `${kv[0]}:${kv[1]}`).join(',')}`);
}, Text);
Text.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create(`@Prop setProp: ${Array.from(ObservedObject.GetRawObject(this.setProp)).join(',')}`);
}, Text);
Text.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create(`@Prop unionProp: ${this.unionProp}`);
}, Text);
Text.pop();
Column.pop();
}
rerender() {
this.updateDirtyElements();
}
}
@Prop
转换前后
转换成TS之后,和@State的操作类似。
- 将原有属性重写为getter和setter
- 声明一个以双下划线开头的私有属性,在getter和setter里访问私有属性的get和set方法
初始化
- @Prop状态变量转换成TS代码之后对应两个不同的类
- SynchedPropertySimpleOneWayPU 由简单类型转换而来,不管这个简单类型是否是由父组件中状态变量的属性传递而来,还是基本类型传递而来。只要声明的时候是简单类型,就会生成SynchedPropertySimpleOneWayPU
- SynchedPropertyObjectOneWayPU 由非简单类型转换而来
传递
命名参数传递过来的是状态的原始值。这里其实是调用的simpleState的getter,simpleState的getter返回的是状态对象ObservedPropertySimplePU里wrapperValue包装的原始值
simpleState的getter内调用__simpleState.get()获取__simpleState包装的原始值
小结:见 框架行为 1.
SynchedPropertyOneWayPU
SynchedPropertySimpleOneWayPU和SynchedPropertySimpleOneWayPU 是SynchedPropertyOneWayPU的子类
至于怎么区分的,我们可以在gitee.com/openharmony...里找到代码
判断逻辑和@State很类似,可以参考 鸿蒙ACE-状态分析@State ObservedPropertySimplePU、ObservedPropertyObjectPU 部分
结论:isBasicType里面判断,如果是string、boolean、number、enum、bigin以及他们的包装类String、Boolean、Number等都是基础类型
关联UI
ObservedPropertyAbstractPU
SynchedPropertyOneWayPU继承自ObservedPropertyAbstractPU,在constructor的时候将视图和属性传入了父类ObservedPropertyAbstractPU
之后的逻辑就和@State关联UI很相似,可以参考 鸿蒙ACE-状态分析@State ObservedPropertyPU 怎么关联UI? 部分
结论:状态变量创建的时候会将视图this传进去,在状态变量对象内部通过owningView持有了视图实例
关联使用状态变量的UI
get方法里调用了recordPropertyDependentUpdate, 这部分可以查看 鸿蒙ACE-状态分析@State
更新UI
set
set方法和@State的set方法很相似,参考鸿蒙ACE-状态分析@State ObservedPropertyPU 怎么更新UI? 部分
结论:
我们写的ArkTS代码经过转换后生成TS代码,创建组件的代码被包装进一个箭头函数传入observeComponentCreation2,然后内部又对传入的这个箭头函数做了一层包装,并且存储下来
之后状态变量发生变化的时候,就拿到它所持有的view,并通过elemId拿到存储下来的更新函数并且调用
小结:框架行为 2.a
- @Prop装饰的变量最终也会生成一个 ObservedPropertyAbstractPU 实例,ObservedPropertyAbstractPU这个类是所有状态数据源的父类。所以@Prop作为数据源和@State的流程非常类似
@Prop 对数据深拷贝
SynchedPropertyOneWayPU
constructor(source: ObservedPropertyAbstract | C, owningChildView: IPropertySubscriber, thisPropertyName: PropertyInfo)
在SynchedPropertyOneWayPU的constructor中我们可以看到,对数据源拷贝使用了resetLocalValue
private resetLocalValue(newObservedObjectValue: C, needCopyObject: boolean): boolean
private copyObject(value: C, propName: string): C
可以看到,在API9以后都是执行的深拷贝
小结:框架行为 1.
数据源更改时,@Prop状态变量怎么更新?
概述
源码里面针对初始化和更新场景有一段解释
我们让GPT翻译一下
初始化场景:
-
没有本地初始化,提供了源(它的ObservedObject值):
- 将ObservedObject包装成一个ObservedPropertyObjectPU。
- 将ObservedObject深拷贝到localCopyObservedObject_ 。
-
本地初始化,没有提供源:
- 应用程序的转译代码调用set。
- 保留source_为undefined。
- 不需要深拷贝,但是提供的本地初始化可能需要包装在一个ObservedObject中以设置到localCopyObservedObject_。
-
本地初始化,提供了源(它的ObservedObject值):
- 当前应用程序的转译代码不是可选的。
- 在构造函数中设置源,如情况1。
- 调用set()来设置源值,但这不会进行深拷贝。
更新场景:
-
分配一个新的Object值:this.aProp = new ClassA()
- rhs可以是ObservedObject,因为@Observed装饰或现在。
- notifyPropertyHasChangedPU。
-
本地ObservedObject成员属性更改
- 调用objectPropertyHasChangedPU,eventSource是存储在localCopyObservedObject_中的ObservedObject。
- 不需要复制,notifyPropertyHasChangedPU。
-
从父级触发的自定义组件的重新渲染
- 调用reset()(由转译器生成的代码),设置source_的值,如果这导致了更改,将调用syncPeerHasChanged。
- syncPeerHasChanged需要将ObservedObject从源深拷贝到localCopyObservedObject_。
- notifyPropertyHasChangedPU。
-
source_ ObservedObject成员属性更改
- 调用objectPropertyHasChangedPU,eventSource是存储在source_.getUnmonitored中的ObservedObject。
- notifyPropertyHasChangedPU。
初始化
初始化的时候使用命名方式传递,注意此时的this.simpleState是一个getter,getter获取的是原始类型的值。比如simpleState是一个number,此时传递给@Prop的还是一个number,只不过@Prop内部又对number进行了包装
更新
updateStateVarsOfChildByElmtId
父组件更新时会调用updateStateVarsOfChildByElmtId
PUV2ViewBase
public updateStateVarsOfChildByElmtId(elmtId, params: Object): void
updateStateVarsOfChildByElmtId内部根据elmtId拿到所需要更新的子组件,然后调用子组件的updateStateVars,并将上一步传入的参数传到updateStateVars中
public abstract updateStateVars(params: Object);
子组件的updateStateVars方法内,各个@Prop状态变量根据传入的参数,调用对应的reset方法
SynchedPropertyOneWayPU
public reset(sourceChangedValue: C): void
reset方法内部,调用了source_的set方法
source_来源
source_是@Prop状态变量初始化时创建的一个类ObservedPropertyObjectPU
ObservedPropertyObjectPU这个类是不是很熟悉,它就是@State状态变量对应的类
name调用set方法就和@State状态变量的set方法很相似了
之后就可以看 鸿蒙ACE-状态分析@State notifyPropertyHasChangedPU、notifyTrackedObjectPropertyHasChanged 部分
ObservedPropertyAbstractPU
protected notifyPropertyHasChangedPU()
set内部会调用 notifyPropertyHasChangedPU
SynchedPropertyOneWayPU
public syncPeerHasChanged(eventSource: ObservedPropertyAbstractPU): void
syncPeerHasChanged 内部又会调用restLocalValue进行深度拷贝并调用notifyPropertyHasChangedPU驱动UI更新
后面就和@State一样了,可以参考 鸿蒙ACE-状态分析@State notifyPropertyHasChangedPU、notifyTrackedObjectPropertyHasChanged 部分
小结:框架行为 2.b.
总结
- @Prop状态变量和@State类似,在编译后,原有变量被重写成getter和setter,同时声明一个私有的变量用于存储原有变量
- @Prop初始化时,在constructor内会调用resetLocalValue对原有数据进行深拷贝
- @Prop变化时和@State变化时是一样的逻辑,一系列逻辑处理完成后会调用notifyPropertyHasChangedPU驱动UI刷新
- 数据源(@State)变化时,会调用@Prop里面的reset,reset内会再次进行深拷贝,最后会调用notifyPropertyHasChangedPU驱动UI刷新
- @Prop内实际的存储是source_,source_是 ObservedPropertyObjectPU类的一个实例,这个实例是@State的状态对应的类。所以@Prop的观察也只能观察一层