鸿蒙ACE-V2状态分析@Provider、@Consumer

160 阅读2分钟

@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装饰器

image.png

public static addProvideConsumeVariableDecoMeta(proto: Object, varName: string, aliasName: string, deco: '@Provider' | '@Consumer'): void {

可以看到元信息对象里面存储了两个属性

  1. 一个是原始属性对应的信息,包括装饰器名称和别名
  2. 一个是自定义的属性,包括原始变量名,别名,装饰器名称

image.png

private static metaAliasKey(aliasName: string, deco: '@Provider' | '@Consumer') : string

这里也给出了上面两个属性各自存储的格式

image.png

@Consumer装饰器

Consume也是一个属性装饰器,和@Provider一样也添加了一些原信息。只不过因为它不是数据源,所以它不用调用trackInternal来初始化状态变量

image.png