【鸿蒙实战开发】@Observed装饰器和@ObjectLink装饰器:嵌套类对象属性变革 ...

打印 上一主题 下一主题

主题 643|帖子 643|积分 1944

@Observed装饰器和@ObjectLink装饰器:嵌套类对象属性变革
媒介

为什么要有@Observed装饰器和@ObjectLink装饰器?
在@Prop,@Link,@Provide与@Consume这几个装饰器仅能观察到第一层的变革,在实际应用开发中,应用会根据开发必要,封装本身的数据模子。对于多层嵌套的情况,比如二维数组,或者数组项class,或者class的属性是class,他们的第二层的属性变革是无法观察到的。这就引出了@Observed/@ObjectLink装饰器的作用。
概述

@ObjectLink和@Observed类装饰器用于在涉及嵌套对象或数组的场景中举行双向数据同步:
●被@Observed装饰的类,可以被观察到属性的变革;
●子组件中@ObjectLink装饰器装饰的状态变量用于吸收@Observed装饰的类的实例,和父组件中对应的状态变量创建双向数据绑定。这个实例可以是数组中的被@Observed装饰的项,或者是class object中的属性,这个属性同样也必要被@Observed装饰。
●单独使用@Observed是没有任何作用的,必要搭配@ObjectLink或者@Prop使用。
注意:

   使用@Observed装饰class会改变class原始的原型链,@Observed和其他类装饰器装饰同一个class可能会带来问题。
  @ObjectLink装饰器不能在@Entry装饰的自界说组件中使用。
  装饰器阐明


@ObjectLink装饰的变量不能被赋值,假如要使用赋值操纵,可以使用@Prop。
●@Prop装饰的变量和数据源的关系是是单向同步,@Prop装饰的变量在本地拷贝了数据源,所以它允许本地更改,假如父组件中的数据源有更新,@Prop装饰的变量本地的修改将被覆盖;
●@ObjectLink装饰的变量和数据源的关系是双向同步,@ObjectLink装饰的变量相当于指向数据源的指针。克制对@ObjectLink装饰的变量赋值,假如一旦发生@ObjectLink装饰的变量的赋值,则同步链将被打断。因为@ObjectLink装饰的变量通过数据源(Object)引用来初始化。对于实现双向数据同步的@ObjectLink,赋值相当于更新父组件中的数组项或者class的属性,TypeScript/JavaScript不能实现,会发生运行时报错。
框架举动

●初始渲染:
●@Observed装饰的class的实例会被不透明的代理对象包装,代理了class上的属性的setter和getter方法
●子组件中@ObjectLink装饰的从父组件初始化,吸收被@Observed装饰的class的实例,@ObjectLink的包装类会将本身注册给@Observed class。
●属性更新:当@Observed装饰的class属性改变时,会走到代理的setter和getter,然后遍历依靠它的@ObjectLink包装类,通知数据更新。
示例:

嵌套对象和数组
  1. @Observed
  2. class Person{
  3.   name:string
  4.   age:number
  5.   gf:Person
  6.   constructor(name:string,age:number,gf?:Person){
  7.     this.name=name
  8.     this.age=age
  9.     this.gf=gf
  10.   }
  11. }
  12. @Component
  13. struct Child{
  14.   @ObjectLink p:Person
  15.   build() {
  16.     Column(){
  17.       Text(`${this.p.name}:${this.p.age}`)
  18.     }
  19.   }
  20. }
  21. @Entry
  22. @Component
  23. struct Parent{
  24.   @State p:Person=new Person('Tom',21,new Person('Rose',18))
  25.   @State gfs:Person[]=[new Person('汤姆',16),new Person('路西',18)]
  26.   build() {
  27.     Column(){
  28.       Child({p:this.p.gf})
  29.         .onClick(()=>this.p.gf.age++)
  30.       Text('-------------------')
  31.       ForEach(
  32.         this.gfs,
  33.         p=>{
  34.           Child({p:p}).onClick(()=>this.p.age++)
  35.         }
  36.       )
  37.     }
  38.   }
  39. }
复制代码
在子组件中给@ObjectLink装饰的变量赋值是不允许的。
  1. @Observed
  2. class ClassA {
  3.   public c: number = 0;
  4.   constructor(c: number) {
  5.     this.c = c;
  6.   }
  7. }
  8. @Component
  9. struct ObjectLinkChild {
  10.   @ObjectLink testNum: ClassA;
  11.   build() {
  12.     Text(`ObjectLinkChild testNum ${this.testNum.c}`)
  13.       .onClick(() => {
  14.         // ObjectLink不能被赋值 这是不允许的,对于实现双向数据同步的@ObjectLink,赋值相当于要更新父组件中的数组项或者class的属性,这个对于 TypeScript/JavaScript是不能实现的。框架对于这种行为会发生运行时报错。
  15.         this.testNum = new ClassA(47);
  16.         // 可以对ObjectLink装饰对象的属性赋值
  17.         //this.testNum.c = 47;
  18.       })
  19.   }
  20. }
  21. @Entry
  22. @Component
  23. struct Parent {
  24.   @State testNum: ClassA[] = [new ClassA(1)];
  25.   build() {
  26.     Column() {
  27.       Text(`Parent testNum ${this.testNum[0].c}`)
  28.         .onClick(() => {
  29.           this.testNum[0].c += 1;
  30.         })
  31.       ObjectLinkChild({ testNum: this.testNum[0] })
  32.     }
  33.   }
  34. }
复制代码
@Prop与@ObjectLink的差别

@ObjectLink装饰的变量是对数据源的引用

代码示例:
  1. let nextId = 1;
  2. @Observed
  3. class SubCounter {
  4.   counter: number;
  5.   constructor(c: number) {
  6.     this.counter = c;
  7.   }
  8. }
  9. @Observed
  10. class ParentCounter {
  11.   id: number;
  12.   counter: number;
  13.   subCounter: SubCounter;
  14.   incrCounter() {
  15.     this.counter++;
  16.   }
  17.   incrSubCounter(c: number) {
  18.     this.subCounter.counter += c;
  19.   }
  20.   setSubCounter(c: number): void {
  21.     this.subCounter.counter = c;
  22.   }
  23.   constructor(c: number) {
  24.     this.id = nextId++;
  25.     this.counter = c;
  26.     this.subCounter = new SubCounter(c);
  27.   }
  28. }
  29. @Component
  30. struct CounterComp {
  31.   @ObjectLink value: ParentCounter;
  32.   build() {
  33.     Column({ space: 10 }) {
  34.       CountChild({ subValue: this.value.subCounter })
  35.       Text(`this.value.counter:increase 7 `)
  36.         .fontSize(30)
  37.         .onClick(() => {
  38.           // click handler, Text(`this.subValue.counter: ${this.subValue.counter}`) will update
  39.           this.value.incrSubCounter(7);
  40.         })
  41.       Divider().height(2)
  42.     }
  43.   }
  44. }
  45. @Component
  46. struct CountChild {
  47.   @ObjectLink subValue: SubCounter;
  48.   build() {
  49.     Text(`this.subValue.counter: ${this.subValue.counter}`)
  50.       .fontSize(30)
  51.   }
  52. }
  53. @Entry
  54. @Component
  55. struct ParentComp {
  56.   @State counter: ParentCounter[] = [new ParentCounter(1), new ParentCounter(2), new ParentCounter(3)];
  57.   build() {
  58.     Row() {
  59.       Column() {
  60.         CounterComp({ value: this.counter[0] })
  61.         CounterComp({ value: this.counter[1] })
  62.         CounterComp({ value: this.counter[2] })
  63.         Divider().height(5)
  64.         ForEach(this.counter,
  65.           (item: ParentCounter) => {
  66.             CounterComp({ value: item })
  67.           },
  68.           (item: ParentCounter) => item.id.toString()
  69.         )
  70.         Divider().height(5)
  71.         Text('Parent: reset entire counter')
  72.           .fontSize(20).height(50)
  73.           .onClick(() => {
  74.             this.counter = [new ParentCounter(1), new ParentCounter(2), new ParentCounter(3)];
  75.           })
  76.         Text('Parent: incr counter[0].counter')
  77.           .fontSize(20).height(50)
  78.           .onClick(() => {
  79.             this.counter[0].incrCounter();
  80.             this.counter[0].incrSubCounter(10);
  81.           })
  82.         Text('Parent: set.counter to 10')
  83.           .fontSize(20).height(50)
  84.           .onClick(() => {
  85.             this.counter[0].setSubCounter(10);
  86.           })
  87.       }
  88.     }
  89.   }
  90. }
复制代码
@Prop装饰变量时会举行深拷贝

假如用@Prop替代@ObjectLink。点击第一个click handler,UI革新正常。但是点击第二个onClick变乱,@Prop 对变量做了一个本地拷贝,CounterComp的第一个Text并不会革新。
this.value.subCounter和this.subValue并不是同一个对象。所以this.value.subCounter的改变,并没有改变this.subValue的拷贝对象,Text(
   this.subValue.counter: ${this.subValue.counter}
  )不会革新。

代码示例:
  1. @Component
  2. struct CounterComp {
  3.   @Prop value: ParentCounter = new ParentCounter(0);
  4.   @Prop subValue: SubCounter = new SubCounter(0);
  5.   build() {
  6.     Column({ space: 10 }) {
  7.       Text(`this.subValue.counter: ${this.subValue.counter}`)
  8.         .fontSize(20)
  9.         .onClick(() => {
  10.           // 1st click handler
  11.           this.subValue.counter += 7;
  12.         })
  13.       Text(`this.value.counter:increase 7 `)
  14.         .fontSize(20)
  15.         .onClick(() => {
  16.           // 2nd click handler
  17.           this.value.incrSubCounter(7);
  18.         })
  19.       Divider().height(2)
  20.     }
  21.   }
  22. }
复制代码
总结:

每个装饰器都有本身可以观察的能力,并不是全部的改变都可以被观察到,只有可以被观察到的变革才会举行UI更新。@Observed装饰器可以观察到嵌套对象的属性变革,其他装饰器仅能观察到第二层的变革。
●@ObjectLink和@Observed类装饰器用于在涉及嵌套对象或数组的场景中举行双向数据同步:
●被@Observed装饰的类,可以被观察到属性的变革;
●子组件中@ObjectLink装饰器装饰的状态变量用于吸收@Observed装饰的类的实例,和父组件中对应的状态变量创建双向数据绑定。
单独使用@Observed是没有任何作用的,必要搭配@ObjectLink或者@Prop使用。
@ObjectLink装饰的变量和数据源的关系是双向同步,@ObjectLink装饰的变量相当于指向数据源的指针。克制对@ObjectLink装饰的变量赋值,假如一旦发生@ObjectLink装饰的变量的赋值,则同步链将被打断。
@Prop与@ObjectLink的差别:

@ObjectLink装饰的变量是对数据源的引用
@Prop装饰变量时会举行深拷贝
写在最后

总的来说,华为鸿蒙不再兼容安卓,对中年步调员来说是一个寻衅,也是一个机会。随着鸿蒙的不断发展以及国家的大力支持,将来鸿蒙职位肯定会迎来一个大的发作,只有积极应对变革,不断学习和提升本身,我们才气在这个变革的期间中立于不败之地。
●假如你觉得这篇内容对你还蛮有资助,我想约请你帮我两个小忙:
●点赞,转发,有你们的 『点赞和批评』,才是我创造的动力。
●关注小编,同时可以等待后续文章ing ,不定期分享原创知识。



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

大连全瓷种植牙齿制作中心

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表