美文网首页
鸿蒙~嵌套数据结构中 @Prop和@ObjectLink之的区别

鸿蒙~嵌套数据结构中 @Prop和@ObjectLink之的区别

作者: 胡修波 | 来源:发表于2024-01-10 08:02 被阅读0次

    以下示例中:

    父组件ViewB渲染@State arrA:Array<ClassA>。@State可以观察新数组的分配、数组项插入、删除和替换。

    子组件ViewA渲染每一个ClassA的对象。

    类装饰器@Observed ClassA与@ObjectLink a: ClassA。

    可以观察嵌套在Array内的ClassA对象的变化。

    不使用@Observed时: ViewB中的this.arrA[Math.floor(this.arrA.length/2)].c=10将不会被观察到,相应的ViewA组件也不会更新。

    对于数组中的第一个和第二个数组项,每个数组项都初始化了两个ViewA的对象,渲染了同一个ViewA实例。在一个ViewA中的属性赋值this.a.c += 1;时不会引发另外一个使用同一个ClassA初始化的ViewA的渲染更新。

    let NextID: number = 1;
    
    // 类装饰器@Observed装饰ClassA
    @Observed
    class ClassA {
      public id: number;
      public c: number;
    
      constructor(c: number) {
        this.id = NextID++;
        this.c = c;
      }
    }
    
    @Component
    struct ViewA {
      @ObjectLink a: ClassA;
      label: string = "ViewA1";
    
      build() {
        Row() {
          Button(`ViewA [${this.label}] this.a.c= ${this.a.c} +1`)
            .onClick(() => {
              // 改变对象属性
              this.a.c += 1;
            })
        }
      }
    }
    
    @Entry
    @Component
    struct ViewB {
      @State arrA: ClassA[] = [new ClassA(0), new ClassA(0)];
    
      build() {
        Column() {
          ForEach(this.arrA,
            (item: ClassA) => {
              ViewA({ label: `#${item.id}`, a: item })
            },
            (item: ClassA): string => { return item.id.toString(); }
          )
    
          Divider().height(10)
    
          if (this.arrA.length) {
            ViewA({ label: `ViewA this.arrA[first]`, a: this.arrA[0] })
            ViewA({ label: `ViewA this.arrA[last]`, a: this.arrA[this.arrA.length-1] })
          }
    
          Divider().height(10)
    
          Button(`ViewB: reset array`)
            .onClick(() => {
              // 替换整个数组,会被@State this.arrA观察到
              this.arrA = [new ClassA(0), new ClassA(0)];
            })
          Button(`array push`)
            .onClick(() => {
              // 数组中插入数据,会被@State this.arrA观察到
              this.arrA.push(new ClassA(0))
            })
          Button(`array shift`)
            .onClick(() => {
              // 数组中移除数据,会被@State this.arrA观察到
              this.arrA.shift()
            })
          Button(`ViewB: chg item property in middle`)
            .onClick(() => {
              // 替换数组中的某个元素,会被@State this.arrA观察到
              this.arrA[Math.floor(this.arrA.length / 2)] = new ClassA(11);
            })
          Button(`ViewB: chg item property in middle`)
            .onClick(() => {
              // 改变数组中某个元素的属性c,会被ViewA中的@ObjectLink观察到
              this.arrA[Math.floor(this.arrA.length / 2)].c = 10;
            })
        }
      }
    }
    

    在ViewA中,将@ObjectLink替换为@Prop。

    @Component
    struct ViewA {
    
      @Prop a: ClassA = new ClassA(0);
      label : string = "ViewA1";
    
      build() {
         Row() {
            Button(`ViewA [${this.label}] this.a.c= ${this.a.c} +1`)
            .onClick(() => {
                // change object property
                this.a.c += 1;
            })
         }
      }
    }
    

    与用@ObjectLink修饰不同,用@ObjectLink修饰时,点击数组的第一个或第二个元素,后面两个ViewA会发生同步的变化。

    @Prop是单向数据同步,ViewA内的Button只会触发Button自身的刷新,不会传播到其他的ViewA实例中。在ViewA中的ClassA只是一个副本,并不是其父组件中@State arrA : Array<ClassA>中的对象,也不是其他ViewA的ClassA,这使得数组的元素和ViewA中的元素表面是传入的同一个对象,实际上在UI上渲染使用的是两个互不相干的对象。

    需要注意@Prop和@ObjectLink还有一个区别:@ObjectLink装饰的变量是仅可读的,不能被赋值;@Prop装饰的变量可以被赋值。

    @ObjectLink实现双向同步,因为它是通过数据源的引用初始化的。

    @Prop是单向同步,需要深拷贝数据源。

    对于@Prop赋值新的对象,就是简单地将本地的值覆写,但是对于实现双向数据同步的@ObjectLink,覆写新的对象相当于要更新数据源中的数组项或者class的属性,这个对于 TypeScript/JavaScript是不能实现的

    相关文章

      网友评论

          本文标题:鸿蒙~嵌套数据结构中 @Prop和@ObjectLink之的区别

          本文链接:https://www.haomeiwen.com/subject/azzandtx.html