@Provider装饰器和@Consumer装饰器:跨组件层级双向同步
developer.huawei.com/consumer/cn…
@Provider和@Consumer用于跨组件层级数据双向同步,可以使得开发者不拘泥于组件层级
@Provider,即数据提供方,其所有的子组件都可以通过@Consumer绑定相同的key来获取@Provider提供的数据。
@Consumer,即数据消费方,可以通过绑定同样的key获取其最近父节点的@Provider的数据,当查找不到@Provider的数据时,使用本地默认值。
@Provider和@Consumer装饰数据类型需要一致
转换前
/**
*
* ProviderConsumer.ets
* Created by unravel on 2024/6/18
* @abstract
*/
@ObservedV2
class User {
@Trace name: string;
@Trace age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
const data: User[] = [new User('Json', 10), new User('Eric', 15)];
@Entry
@ComponentV2
export struct ProviderConsumer {
@Provider('data') users: User[] = data;
build() {
Column() {
Child()
Button('age new user')
.onClick(() => {
this.users.push(new User('Molly', 18));
})
Button('age++')
.onClick(() => {
this.users[0].age++;
})
Button('change name')
.onClick(() => {
this.users[0].name = 'Shelly';
})
}
}
}
@ComponentV2
struct Child {
@Consumer('data') users: User[] = [];
build() {
Column() {
ForEach(this.users, (item: User) => {
Column() {
Text(`name: ${item.name}`).fontSize(30)
Text(`age: ${item.age}`).fontSize(30)
Divider()
}
})
}
}
}
转换后
if (!("finalizeConstruction" in ViewPU.prototype)) {
Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { });
}
/**
*
* ProviderConsumer.ets
* Created by unravel on 2024/6/18
* @abstract
*/
@ObservedV2
class User {
@Trace
name: string;
@Trace
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
const data: User[] = [new User('Json', 10), new User('Eric', 15)];
export class ProviderConsumer extends ViewV2 {
constructor(parent, params, __localStorage, elmtId = -1, paramsLambda, extraInfo) {
super(parent, elmtId, extraInfo);
this.users = data;
this.finalizeConstruction();
}
@Provider('data')
users: User[];
initialRender() {
this.observeComponentCreation2((elmtId, isInitialRender) => {
Column.create();
}, Column);
{
this.observeComponentCreation2((elmtId, isInitialRender) => {
if (isInitialRender) {
let componentCall = new Child(this, {}, undefined, elmtId, () => { }, { page: "entry/src/main/ets/pages/statev2/ProviderConsumer.ets", line: 27 });
ViewV2.create(componentCall);
let paramsLambda = () => {
return {};
};
componentCall.paramsGenerator_ = paramsLambda;
}
else {
this.updateStateVarsOfChildByElmtId(elmtId, {});
}
}, { name: "Child" });
}
this.observeComponentCreation2((elmtId, isInitialRender) => {
Button.createWithLabel('age new user');
Button.onClick(() => {
this.users.push(new User('Molly', 18));
});
}, Button);
Button.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Button.createWithLabel('age++');
Button.onClick(() => {
this.users[0].age++;
});
}, Button);
Button.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Button.createWithLabel('change name');
Button.onClick(() => {
this.users[0].name = 'Shelly';
});
}, Button);
Button.pop();
Column.pop();
}
rerender() {
this.updateDirtyElements();
}
static getEntryName(): string {
return "ProviderConsumer";
}
}
class Child extends ViewV2 {
constructor(parent, params, __localStorage, elmtId = -1, paramsLambda, extraInfo) {
super(parent, elmtId, extraInfo);
this.users = [];
this.finalizeConstruction();
}
@Consumer('data')
users: User[];
initialRender() {
this.observeComponentCreation2((elmtId, isInitialRender) => {
Column.create();
}, Column);
this.observeComponentCreation2((elmtId, isInitialRender) => {
ForEach.create();
const forEachItemGenFunction = _item => {
const item = _item;
this.observeComponentCreation2((elmtId, isInitialRender) => {
Column.create();
}, Column);
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create(`name: ${item.name}`);
Text.fontSize(30);
}, Text);
Text.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Text.create(`age: ${item.age}`);
Text.fontSize(30);
}, Text);
Text.pop();
this.observeComponentCreation2((elmtId, isInitialRender) => {
Divider.create();
}, Divider);
Column.pop();
};
this.forEachUpdateFunction(elmtId, this.users, forEachItemGenFunction);
}, ForEach);
ForEach.pop();
Column.pop();
}
rerender() {
this.updateDirtyElements();
}
}
registerNamedRoute(() => new ProviderConsumer(undefined, {}), "", { bundleName: "com.unravel.myapplication", moduleName: "entry", pagePath: "pages/statev2/ProviderConsumer", pageFullPath: "entry/src/main/ets/pages/statev2/ProviderConsumer", integratedHsp: "false" });
Provider装饰器
Provider是一个变量装饰器,它除了给源对象添加了一些信息,最终的实现和@Trace、@Local一样都是调用的trackInternal
关于trackInternal的实现参考 鸿蒙ACE-V2状态分析@ObservedV2、@Trace装饰器
public static addProvideConsumeVariableDecoMeta(proto: Object, varName: string, aliasName: string, deco: '@Provider' | '@Consumer'): void {
可以看到元信息对象里面存储了两个属性
- 一个是原始属性对应的信息,包括装饰器名称和别名
- 一个是自定义的属性,包括原始变量名,别名,装饰器名称
private static metaAliasKey(aliasName: string, deco: '@Provider' | '@Consumer') : string
这里也给出了上面两个属性各自存储的格式
@Consumer装饰器
Consume也是一个属性装饰器,和@Provider一样也添加了一些原信息。只不过因为它不是数据源,所以它不用调用trackInternal来初始化状态变量