鸿蒙装饰器篇-状态装饰器

528 阅读3分钟
前言

并不是状态变量的所有更改都会引起UI的刷新,只有可以被框架监听到的修改才会引起UI刷新。下面会对对应的装饰器进行简单的测试,确认那种数据是可以被监听的。

装饰器@State

定义(装饰)变量,绑定该变量的UI会随着变量值的变化而变化,支持当前变量修改以及第一层数据的修改的监听,属于是浅监听

  • 装饰普通数据类型数据,修改数据都是可以正常监听的
  • 装饰Object类型数据,支持当前变量修改以及第一层数据的修改的监听,如下当只修改this.obj.sub.b的值是UI不会发生改变,经过打印数据已经被修改成功。
interface ObjT {
  a?:string,
  sub: {
    b:string
  }
}

@Entry
@Component
struct Index {
  @State obj: ObjT = {
    a:'1',
    sub: {
      b: '1'
    }
  }

  build() {
    Row() {
      Column() {
        Button(`按钮${this.obj.a}${this.obj.sub.b}`)
          .onClick(() => {
            this.obj.sub.b = '-'
            this.obj.a = '2'
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}
  • 装饰的对象是array时,可以观察到数组本身的赋值和添加、删除、更新数组的变化。但是数组单项里面的数据发生改变的时候不会被监听到。

  • 装饰class对象类型的变量时,可以观察到自身的赋值的变化,和其属性赋值的变化。但不支持嵌套的属性赋值观察不到。

class ClassA {
  public value: string;

  constructor(value: string) {
    this.value = value;
  }
}

class Model {
  public value: string;
  public name: ClassA;
  constructor(value: string, a: ClassA) {
    this.value = value;
    this.name = a;
  }
}

@State title: Model = new Model('Hello', new ClassA('World'));

// 可以被监听
this.title.value = 'Hi';

// 嵌套的属性赋值观察不到
this.title.name.value = 'ArkUI';
装饰器@Prop

定义(装饰)从父组件传入当前组件的变量,单向数据流即父组件变量变化会影响到当前变量,当前变量也可以单独修改但是变化不会同步回其父组件。

@Component
struct Sec {
  @Prop msg:string //接收父组件message变量
  build() {
    Button(this.msg)
      .fontSize(50)
      .fontWeight(FontWeight.Bold)
      .onClick(() => {
        this.msg = '2222' // 修改当前组件数据,父组件数据不会改变
      })
  }
}

@Entry
@Component
struct Index {
  @State message: string = 'Hello World'

  build() {
    Row() {
      Column() {
        Button(`按钮${this.message}`)
          .onClick(() => {
            this.message = '8888' //修改会同步子组件
          })
        Sec({msg: this.message})
      }
    }
  }
}
装饰器@Link

使用同@Prop,特点是可以父子双向同步,传值的方式稍微不同需要是$开头

@Component
struct Sec {
  @Link msg:string //接收父组件message变量
  build() {
    Button(this.msg)
      .fontSize(50)
      .fontWeight(FontWeight.Bold)
      .onClick(() => {
        this.msg = '2222' //修改会同步父组件
      })
  }
}

@Entry
@Component
struct Index {
  @State message: string = 'Hello World'

  build() {
    Row() {
      Column() {
        Button(`按钮${this.message}`)
          .onClick(() => {
            this.message = '8888' //修改会同步子组件
          })
        Sec({msg: $message}) // 传值是需是$开头
      }
    }
  }
}
装饰器@Provide与@Consume

前端童鞋对这个东西应该很熟悉🤭。上面@Prop与@Link用于父子组件传值。@Provide与@Consume组合是用来后代组件双向同步的,简单说就是不仅支持父子组件,还支持爷孙组件,祖孙组件。。。

使用起来很简单,在上级组件@Provide定义变量,且不需要一直向下传递,哪里需要哪里使用@Consume接收就行。

@Component
struct Sec {

  build() {
    Three()
  }
}

@Component
struct Three {
  @Consume message: string; // 接收Provide传递的数据
  build() {
    Column() {
      Text('2222')
        .fontSize(50)
        .fontWeight(FontWeight.Bold)
      Button(this.message)
        .fontSize(50)
        .onClick(() => {
          this.message = '2222' // 孙组件修改并同步祖父组件
        })
    }
  }
}

@Entry
@Component
struct Index {
  @Provide  message: string = 'Hello World' // 定义需要传递的变量,不需要一直传递,那个下级组件需要在哪里接数据就行

  build() {
    Row() {
      Column() {
        Button(`按钮${this.message}`)
          .onClick(() => {
            this.message = '8888' // 祖父组件修改并同步孙组件
          })
        Sec()
      }
      .width('100%')
    }
    .height('100%')
  }
}
装饰器@Observed@ObjectLink

了解@State装饰器的时候,发现数据监听只会监听修改自己和第一层的变化,如果需要监听深层数据的话就需要用到这俩装饰器。在使用过程中发现,灵活度不高(也可能之前一直在用js),感觉写法比较死板。

首先官方给给出以下两个限制:

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

首先测试一下深层监听,不能用在注意入口组件而且不能直接初始化数据