ComponentV2下的Local、Param、Once、Event、Provider、Consumer、Monitor、Computed的使用。

ComponentV2装饰器:自定义组件

ComponentV2装饰器用于装饰自定义组件,需要使用全新的状态变量装饰器,包括@Local、@Param、@Once、@Event、@Provider、@Consumer等。

最简单的自定义组件至少需要包含以下部分。

@ComponentV2 // 装饰器
struct Index { // struct声明的数据结构
  build() { // build定义的UI
  }
}

Local装饰器:组件内部状态

1、被@Local装饰的变量无法从外部初始化,因此必须在组件内部进行初始化;

2、@Local支持观测number、boolean、string、Object、class等基本类型以及Array、Set、Map、Date等内嵌类型;

3、@Local的观测能力仅限于被装饰的变量本身。当装饰简单类型时,能够观测到对变量的赋值;当装饰对象类型时,仅能观测到对对象整体的赋值;当装饰数组类型时,能观测到数组整体以及数组元素项的变化;当装饰Array、Set、Map、Date等内嵌类型时,可以观测到通过API调用带来的变化;

4、@Local支持null、undefined以及联合类型。

装饰Date类型变量

当装饰的对象是Date时,可以观察到Date整体的赋值,同时可通过调用Date的接口setFullYear, setMonth, setDate, setHours, setMinutes, setSeconds, setMilliseconds, setTime, setUTCFullYear, setUTCMonth, setUTCDate, setUTCHours, setUTCMinutes, setUTCSeconds, setUTCMilliseconds 更新Date的属性。

装饰Map类型变量

当装饰的对象是Map时,可以观察到对Map整体的赋值,同时可以通过调用Map的接口 set、clear、delete更新Map中的数据。

装饰Set类型变量

当装饰的对象是Set时,可以观察到对Set整体的赋值,同时可以通过调用Set的接口add、clear、delete更新Set中的数据。

联合类型

@Local支持null、undefined以及联合类型。在下面的示例中,count类型为number | undefined,点击改变count的类型,UI会随之刷新。

Param:组件外部输入

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

1、Param装饰的变量支持本地初始化,但不允许在组件内部直接修改;

2、被@Param装饰的变量能够在初始化自定义组件时从外部传入,当数据源也是状态变量时,数据源的修改会同步给@Param;

3、@Param可以接受任意类型的数据源,包括普通变量、状态变量、常量、函数返回值等;

4、@Param支持观测number、boolean、string、Object、class等基本类型以及Array、Set、Map、Date等内嵌类型;

5、对于复杂类型如类对象,@Param会接受数据源的引用。在组件内可以修改类对象中的属性,该修改会同步到数据源;

6、@Param的观测能力仅限于被装饰的变量本身。当装饰简单类型时,可以观测变量的整体改变。当装饰对象类型时,仅能观测对象整体的改变。当装饰数组类型时,可以观测数组整体和元素项的改变。当装饰Array、Set、Map、Date等内嵌类型时,可以观测到通过API调用带来的变化;

7、@Param支持null、undefined以及联合类型。

Once:初始化同步一次

Once装饰器在变量初始化时接受外部传入值进行初始化,后续数据源更改不会同步给子组件:

1、@Once必须搭配@Param使用,单独使用或搭配其他装饰器使用都是不允许的。

2、@Once不影响@Param的观测能力,仅针对数据源的变化做拦截。

3、@Once与@Param装饰变量的先后顺序不影响使用功能。

4、@Once与@Param搭配使用时,可以在本地修改@Param变量的值。

Event装饰器:规范组件输出

由于@Param装饰的变量在本地无法更改,使用@Event装饰器装饰回调方法并调用,可以实现更改数据源的变量,再通过@Local的同步机制,将修改同步回@Param,以此达到主动更新@Param装饰变量的效果。

1、@Event装饰的回调方法中参数以及返回值由开发者决定。

2、@Event装饰非回调类型的变量不会生效。当@Event没有初始化时,会自动生成一个空的函数作为默认回调。

3、当@Event未被外部初始化,但本地有默认值时,会使用本地默认的函数进行处理。

4、@Param标志着组件的输入,表明该变量受父组件影响,而@Event标志着组件的输出,可以通过该方法影响父组件。使用@Event装饰回调方法是一种规范,表明该回调作为自定义组件的输出。父组件需要判断是否提供对应方法用于子组件更改@Param变量的数据源。

更改父组件中变量

使用@Event可以更改父组件中变量,当该变量作为子组件@Param变量的数据源时,该变化会同步回子组件的@Param变量。

@ComponentV2
export struct paramTestComponent{
  @Require @Param isShowName:boolean
  @Event setShow:(isShow:boolean)=>void = ()=>{}
  build() {
    Column(){
      if (this.isShowName){
        Text("是否显示!")
          .fontSize(20)
      }
      Button("显示")
        .onClick(()=>{
          // this.isShowName = true    Cannot assign to 'isShowName' because it is a read-only property
          this.setShow(!this.isShowName)
        })
    }.width(300)
    .height(300)
    .margin({left:20})
  }
}
@Entry
@ComponentV2
struct Index {
  @Local isShow:boolean = false
  build() {
     Column({space:20}){
      paramTestComponent({isShowName:this.isShow,setShow:(isShow:boolean)=>{this.isShow = isShow}})

     }.justifyContent(FlexAlign.Start)
    .alignItems(HorizontalAlign.Start)
    .padding({left:20})
  }
}

Provider装饰器和@Consumer装饰器:跨组件层级双向同步

@Provider和@Consumer提供了跨组件层级数据双向同步的能力,

@Provider支持属性的类型可以为number、string、boolean、class、Array、Date、Map、Set等类型。支持装饰箭头函数,需要本地初始化。

@Consumer属性装饰器,可装饰的变量自定义组件中成员变量。属性的类型可以为number、string、boolean、class、Array、Date、Map、Set等类型。支持装饰箭头函数,必须本地初始化。

使用限制

  1. @Provider和@Consumer为自定义组件的属性装饰器,只能装饰自定义组件内的属性,不能装饰class的属性。
  2. @Provider和@Consumer为状态管理V2装饰器,只能在@ComponentV2中使用,不能在@Component中使用。
  3. @Provider和@Consumer只支持本地初始化,不支持外部传入初始化。

使用场景

@Provider和@Consumer双向同步

  1. 点击index中的Button,改变@Provider装饰的text,通知其对应的@Consumer,对应UI刷新。
  2. 点击Child中Button,改变@Consumer装饰的text,通知其对应的@Provider,对应UI刷新。
  3. @ComponentV2
    struct child{
    //如果message没找到,将显示默认值“My own news!”
      @Consumer('message') text:string = "My own news!"
      build() {
        Column({space:20}){
          Text('我是子组件:'+this.text)
            .fontSize(20)
          Button('子组件改变值').onClick(()=>{
            this.text = "孩子说,这是一条消息"
          })
    
        }.justifyContent(FlexAlign.Start)
        .alignItems(HorizontalAlign.Start)
      }
    }
    @Entry
    @ComponentV2
    struct Index {
      @Provider("message") text:string = "this is a message!"
      build() {
         Column({space:20}){
           Text('我是父组件:'+this.text)
             .fontSize(20)
           Button('父组件改变值').onClick(()=>{
             this.text = "父亲说,这是一条消息"
           })
           Divider()
           child()
    
         }.justifyContent(FlexAlign.Start)
        .alignItems(HorizontalAlign.Start)
        .padding({left:20})
      }
    }

    @Provider和@Consumer装饰回调事件.

通过点击按钮给回调函数,传去名字

@ComponentV2
struct setNameComponent{
  @Consumer("getName") setName:(name:string)=>void = (name:string)=>{}
  build() {
    Button('我来给你提供名字!').onClick(()=>{
      this.setName("拉斐尔")
    })
  }
}
@Entry
@ComponentV2
struct Index {
  @Local name:string = ""
  @Provider("getName") getMyName:(name:string)=>void = (name:string)=>{
    this.name = name
}
  build() {
     Column({space:20}){
      Text('我的名字是:'+this.name)
        .fontSize(20)
       Divider()
       setNameComponent()

     }.justifyContent(FlexAlign.Start)
    .alignItems(HorizontalAlign.Start)
    .padding({left:20})
  }
}

        

@Provider和@Consumer装饰复杂类型,配合@Trace一起使用

  1. @Provider和@Consumer只能观察到数据本身的变化。如果需要观察其装饰的复杂数据类型的属性变化,必须配合@Trace一起使用。
  2. 装饰内置类型:Array、Map、Set、Date时,可以观察到某些API的变化,观察能力同@Trace

@Provider重名时,@Consumer向上查找其最近的@Provider

@Provider可以在组件树上重名,@Consumer会向上查找其最近父节点的@Provider的数据。

组件一和二同样发出了‘NameRep’,组件三只接受组件二的消息.

@ComponentV2
struct parent{
  @Provider('NameRep') myName:string = "我是组件二"
  @Consumer('NameRep') getName:string = "我是组件二"

  build() {
    Column(){
      Text('我是组件二接收:'+this.getName)
        .fontSize(20)
      Divider()
      child1()
    }
  }
}
@ComponentV2
struct child1{
  @Consumer('NameRep') getName:string = "我是组件三"

  build() {
    Column(){
      Text('我是组件三接收:'+this.getName)
        .fontSize(20)
      Divider()
    }
  }
}
@Entry
@ComponentV2
struct Index {
  @Provider('NameRep') myName:string = "我是组件一"

  build() {
     Column({space:20}){
       Text(this.myName)
         .fontSize(20)
       Divider()
       parent()

     }.justifyContent(FlexAlign.Start)
    .alignItems(HorizontalAlign.Start)
    .padding({left:20})
  }
}

Monitor装饰器:状态变量修改监听

为了增强状态管理框架对状态变量变化的监听能力,开发者可以使用@Monitor装饰器对状态变量进行监听。@Monitor提供了对V2状态变量的监听。

@Monitor装饰器支持在@ComponentV2装饰的自定义组件和类中使用。

单个@Monitor装饰器能够同时监听多个属性的变化,当这些属性在一次事件中共同变化时,只会触发一次@Monitor的回调方法。

@Monitor装饰器具有深度监听的能力,能够监听嵌套类、多维数组、对象数组中指定项的变化。对于嵌套类、对象数组中成员属性变化的监听要求该类被@ObservedV2装饰且该属性被@Trace装饰。

在继承类场景中,可以在父子组件中对同一个属性分别定义@Monitor进行监听,当属性变化时,父子组件中定义的@Monitor回调均会被调用。

在@ComponentV2装饰的自定义组件中使用@Monitor

Monitor监听local修饰的message,当点击翻译按钮,onMessageChange会被调用。当监听对象变为class类时,未被trace修饰。当点击‘刷新info’按钮时,onInfoChange事件被调用,点击‘刷新info.name’时onInfoNameChange无响应。
@Entry
@ComponentV2
struct Index {
@Local message:string = "this is a message!"
  @Monitor('message')
  onMessageChange(monitor:IMonitor){
    console.log(''+monitor.value()?.before)
    console.log(''+monitor.value()?.now)
  }
  @Local info:info = new info()
  @Monitor('info')
  onInfoChange(m:IMonitor){
    console.log(''+m.value()?.before)
    console.log(''+m.value()?.now)
  }
  @Monitor('info.name')
  onInfoNameChange(m:IMonitor){
    console.log(''+m.value()?.before)
    console.log(''+m.value()?.now)
  }
  build() {
     Column({space:20}){
       Text(this.message)
         .fontSize(20)
       Button('翻译').onClick(()=>{
         this.message = "这是一条消息"
       })
       Button('刷新info').onClick(()=>{
         this.info = new info()
       })
       Button('刷新info.name').onClick(()=>{
         this.info.name = "米开朗琪罗"
       })

     }.justifyContent(FlexAlign.Start)
    .alignItems(HorizontalAlign.Start)
    .padding({left:20})
  }
」

在@ObservedV2装饰的类中使用@Monitor

使用@Monitor监听的属性发生变化时,会触发@Monitor的回调方法。

  • @Monitor监听的对象属性需要被@Trace装饰,未被@Trace装饰的属性的变化无法被监听。@Monitor可以同时监听多个属性,这些属性之间用","隔开。

类中定义的@Monitor随着类的销毁失效。而由于类的实际销毁释放依赖于垃圾回收机制,因此会出现即使所在自定义组件已经销毁,类却还未及时销毁,导致类中定义的@Monitor仍在监听变化的情况。

Computed装饰器:计算属性

计算属性,当被计算的属性变化时,只会计算一次。这解决了UI多次重用该属性导致的重复计算和性能问题。

当被计算的属性变化时,@Computed装饰的getter访问器只会被求解一次

@Entry
@ComponentV2
struct Index {
  @Local firstName:string = "拉斐尔"
  @Local lastName:string = "桑西"
  @Computed
  get FullName(){
    return this.firstName + '.' +this.lastName
  }
  build() {
     Column({space:20}){
       Text(this.FullName)
         .fontSize(20)
       Button('改第一个名').onClick(()=>{
         this.firstName = '小拉斐尔'
       })
       Button('改第二个名').onClick(()=>{
         this.lastName = '桑冬'
       })

     }.justifyContent(FlexAlign.Start)
    .alignItems(HorizontalAlign.Start)
    .padding({left:20})
  }
}

ReusableV2装饰器:组件复用

@ReusableV2同样提供了aboutToRecycle和aboutToReuse的生命周期,在组件被回收时调用aboutToRecycle,在组件被复用时调用aboutToReuse,但与@Reusable不同的是,aboutToReuse没有入参。

在回收阶段,会递归地调用所有子组件的aboutToRecycle回调(即使子组件未被标记可复用);在复用阶段,会递归地调用所有子组件的aboutToReuse回调(即使子组件未被标记可复用)。

@ReusableV2装饰的自定义组件会在被回收期间保持冻结状态,即无法触发UI刷新、无法触发@Monitor回调,与freezeWhenInactive标记位不同的是,在解除冻结状态后,不会触发延后的刷新。

@ReusableV2装饰的自定义组件会在复用时自动重置组件内状态变量的值、重新计算组件内@Computed以及与之相关的@Monitor。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值