鸿蒙ACE-V2状态分析@Param

75 阅读2分钟

@Param:组件外部输入

developer.huawei.com/consumer/cn…

@Param表示组件从外部传入的状态,使得父子组件之间的数据能够进行同步

转换前

/**
 *
 * ParamCmp.ets
 * Created by unravel on 2024/6/18
 * @abstract
 */
@ObservedV2
class Region {
  @Trace x: number;
  @Trace y: number;

  constructor(x: number, y: number) {
    this.x = x;
    this.y = y;
  }
}

@ObservedV2
class Info {
  @Trace name: string;
  @Trace age: number;
  @Trace region: Region;

  constructor(name: string, age: number, x: number, y: number) {
    this.name = name;
    this.age = age;
    this.region = new Region(x, y);
  }
}

@ComponentV2
export struct ParamCmp {
  @Local infoList: Info[] = [new Info("Alice", 8, 0, 0), new Info("Barry", 10, 1, 20), new Info("Cindy", 18, 24, 40)];

  build() {
    Column() {
      ForEach(this.infoList, (info: Info) => {
        MiddleComponent({ info: info })
      })
      Button("change")
        .onClick(() => {
          this.infoList[0] = new Info("Atom", 40, 27, 90);
          this.infoList[1].name = "Bob";
          this.infoList[2].region = new Region(7, 9);
        })
    }
  }
}

@ComponentV2
struct MiddleComponent {
  @Require @Param info: Info;

  build() {
    Column() {
      Text(`name: ${this.info.name}`)
      Text(`age: ${this.info.age}`)
      SubComponent({ region: this.info.region })
    }
  }
}

@ComponentV2
struct SubComponent {
  @Require @Param region: Region;

  build() {
    Column() {
      Text(`region: ${this.region.x}-${this.region.y}`)
    }
  }
}

转换后

if (!("finalizeConstruction" in ViewPU.prototype)) {
    Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { });
}
/**
 *
 * ParamCmp.ets
 * Created by unravel on 2024/6/18
 * @abstract
 */
@ObservedV2
class Region {
    @Trace
    x: number;
    @Trace
    y: number;
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }
}
@ObservedV2
class Info {
    @Trace
    name: string;
    @Trace
    age: number;
    @Trace
    region: Region;
    constructor(name: string, age: number, x: number, y: number) {
        this.name = name;
        this.age = age;
        this.region = new Region(x, y);
    }
}
export class ParamCmp extends ViewV2 {
    constructor(parent, params, __localStorage, elmtId = -1, paramsLambda, extraInfo) {
        super(parent, elmtId, extraInfo);
        this.infoList = [new Info("Alice", 8, 0, 0), new Info("Barry", 10, 1, 20), new Info("Cindy", 18, 24, 40)];
        this.finalizeConstruction();
    }
    @Local
    infoList: Info[];
    initialRender() {
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Column.create();
        }, Column);
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            ForEach.create();
            const forEachItemGenFunction = _item => {
                const info = _item;
                {
                    this.observeComponentCreation2((elmtId, isInitialRender) => {
                        if (isInitialRender) {
                            let componentCall = new MiddleComponent(this, { info: info }, undefined, elmtId, () => { }, { page: "entry/src/main/ets/pages/statev2/ParamCmp.ets", line: 38 });
                            ViewV2.create(componentCall);
                            let paramsLambda = () => {
                                return {
                                    info: info
                                };
                            };
                            componentCall.paramsGenerator_ = paramsLambda;
                        }
                        else {
                            this.updateStateVarsOfChildByElmtId(elmtId, {
                                info: info
                            });
                        }
                    }, { name: "MiddleComponent" });
                }
            };
            this.forEachUpdateFunction(elmtId, this.infoList, forEachItemGenFunction);
        }, ForEach);
        ForEach.pop();
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Button.createWithLabel("change");
            Button.onClick(() => {
                this.infoList[0] = new Info("Atom", 40, 27, 90);
                this.infoList[1].name = "Bob";
                this.infoList[2].region = new Region(7, 9);
            });
        }, Button);
        Button.pop();
        Column.pop();
    }
    rerender() {
        this.updateDirtyElements();
    }
}
class MiddleComponent extends ViewV2 {
    constructor(parent, params, __localStorage, elmtId = -1, paramsLambda, extraInfo) {
        super(parent, elmtId, extraInfo);
        this.initParam("info", (params && "info" in params) ? params.info : undefined);
        this.finalizeConstruction();
    }
    @Param
    readonly info: Info;
    initialRender() {
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Column.create();
        }, Column);
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(`name: ${this.info.name}`);
        }, Text);
        Text.pop();
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(`age: ${this.info.age}`);
        }, Text);
        Text.pop();
        {
            this.observeComponentCreation2((elmtId, isInitialRender) => {
                if (isInitialRender) {
                    let componentCall = new SubComponent(this, { region: this.info.region }, undefined, elmtId, () => { }, { page: "entry/src/main/ets/pages/statev2/ParamCmp.ets", line: 58 });
                    ViewV2.create(componentCall);
                    let paramsLambda = () => {
                        return {
                            region: this.info.region
                        };
                    };
                    componentCall.paramsGenerator_ = paramsLambda;
                }
                else {
                    this.updateStateVarsOfChildByElmtId(elmtId, {
                        region: this.info.region
                    });
                }
            }, { name: "SubComponent" });
        }
        Column.pop();
    }
    public updateStateVars(params) {
        if (params === undefined) {
            return;
        }
        if ("info" in params) {
            this.updateParam("info", params.info);
        }
    }
    rerender() {
        this.updateDirtyElements();
    }
}
class SubComponent extends ViewV2 {
    constructor(parent, params, __localStorage, elmtId = -1, paramsLambda, extraInfo) {
        super(parent, elmtId, extraInfo);
        this.initParam("region", (params && "region" in params) ? params.region : undefined);
        this.finalizeConstruction();
    }
    @Param
    readonly region: Region;
    initialRender() {
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Column.create();
        }, Column);
        this.observeComponentCreation2((elmtId, isInitialRender) => {
            Text.create(`region: ${this.region.x}-${this.region.y}`);
        }, Text);
        Text.pop();
        Column.pop();
    }
    public updateStateVars(params) {
        if (params === undefined) {
            return;
        }
        if ("region" in params) {
            this.updateParam("region", params.region);
        }
    }
    rerender() {
        this.updateDirtyElements();
    }
}

对比

代码转换前后没有太大的变化,@Param装饰的属性变成了readonly的。即我们只能访问@Param变量和它的属性,不能对@Param属性进行重新赋值

image.png

Param装饰器

和@Trace、@Local的实现很相似。只是限制了@Param只能调用get不能调用set

关于trackInternal的实现参考 鸿蒙ACE-V2状态分析@ObservedV2、@Trace装饰器

image.png