ArkUI-状态管理-@State、@Porp、@Link

组件状态管理

ArkUI的组件状态管理,提供了@State@Prop@Link@provide@Consume@Observed@ObjectLink等装饰器。

@State装饰器:组件内状态

在状态变量相关装饰器中,@State是最基础的,使变量拥有状态属性的装饰器,他也是大部分状态变量的数据源。

概述

@State装饰的变量,与声明式范式中的其他被装饰变量一样,是私有的,只能从组件内部访问,在声明时必须指定其类型和本地初始化。初始化也可以选择使用命名参数机制从父组件完成初始化。

@State装饰的变量拥有以下特点:

  • @State装饰的变量与子组件中的@Prop装饰变量之间建立单向数据同步,与@Link、@ObjectLink装饰变量之间建立双向数据同步。
  • @State装饰的变量的生命周期与其所属的自定义组件的生命周期相同。

观察变化

  • 当装饰的数据为string、boolean、number类型时,可以观察到数值的变化。
  • 当装饰的数据类型为class或者object时,可以观察到自身的赋值的变化,和其属性的变化,即Object.keys(observedObject)返回的所有属性。当class中嵌套class时,嵌套属性的赋值是观察不到的。
  • 当装饰的对象是array时,可以观察到数组本身的赋值、添加、删除、更新数组的变化。数组项中的属性的赋值观察不到。
  • 当装饰的对象是Data时,可以观察到Data整体的赋值,同时可通过调用Date的接口setFullYear, setMonth, setDate, setHours, setMinutes, setSeconds, setMilliseconds, setTime, setUTCFullYear, setUTCMonth, setUTCDate, setUTCHours, setUTCMinutes, setUTCSeconds, setUTCMilliseconds 更新Date的属性。
  • 当装饰的变量是Map时,可以观察到Map整体的赋值,同时可通过调用Map的接口set、clear、delete更新Map的值。
  • 当装饰的变量时Set时,可以观察到Set整体的赋值,同时可通过调用Set的接口add、clear、delete更新Set的值。

框架行为

  • 当状态变量被修改时,查询依赖该状态变量的组件。
  • 执行依赖该状态变量的组件的更新方法,组件更新渲染。
  • 和该状态组件不相关的组件和UI描述不会重新渲染。

注意事项

箭头函数体内的this对象,就是定义该函数时所在的作用域指向的对象,而不是使用时所在的作用域指向的对象。 所以当使用箭头函数修改被装饰器@State代理的状态时,要注意。

例如在被@State修饰的class中,声明一个箭头函数,修改class中的某个属性,调用后不生效。

@Prop装饰器:父子单向同步

  • @Prop装饰的变量可以和父组件建立单向的同步关系。
  • @Prop装饰的变量是可变的,但是变化不会同步回其父组件。
  • 当数据源更改时,@Prop装饰的变量都会更新,并且会覆盖本地所有更改。

限制条件

  • @Prop装饰变量时会进行深拷贝,在拷贝的过程中除了基本类型、Map、Set、Data、Array外,都会丢失类型。
  • @Prop装饰器不能在@Entry装饰的自定义组件中使用。

观察变化

  • 当装饰的类型是允许的类型,即Object、class、string、number、boolean、enum类型都可以观察到赋值的变化。
  • 当装饰的类型是Object或者class复杂类型时,可以观察到第一层属性的变化。如果嵌套class中的每一层都是被@Observed装饰的,可以观察到嵌套属性的变化。
  • 当装饰的类型是数组时,可以观察到数组本身的赋值和数据项的添加、删除和更新。
  • 当装饰的变量是Map时,可以观察到Map整体的赋值,同时可通过调用Map的接口set, clear, delete 更新Map的值。
  • 当装饰的变量是Set时,可以观察到Set整体的赋值,同时可通过调用Set的接口add, clear, delete 更新Set的值。

对于@State和@Prop的同步场景:

  • 使用父组件中@State变量初始化子组件中的@Prop变量。当@State变量变化时,该变量值也会同步更新至@Prop变量。
  • @Prop装饰的变量修改,不会影响其数据源@State装饰的变量值。
  • 除了@State,数据源也可以使用@Prop和@Link装饰,对@Prop的同步机制是一样的。
  • 数据源和@Prop变量的类型需要相同,@Prop允许简单类型和class类型。

框架行为

要理解@Prop变量初始化和更新机制,需要了解父组件和拥有@Prop变量的子组件初始渲染和更新流程。

初始渲染:

  • 执行父组件的build()函数将创建子组件的新实例,将数据源传递给子组件。
  • 初始化子组件@Prop装饰的变量。

更新:

  • 子组件@Prop更新时,更新仅停留在当前子组件,不会同步回父组件。
  • 当父组件的数据源更新时,子组件的@Prop装饰的变量将会被来自父组件的数据源重置,所有@Prop装饰的本地的修改将被父组件的更新覆盖。

注意事项:
@Prop装饰的数据更新依赖其所属的自定义组件的重新渲染,所以在应用进入后台时,无法刷新。
如需后台刷新,可使用@Link代替。

@Link装饰器:父子双向同步

子组件中被@Link装饰的变量与其父组件中对应的数据源建立双向数据绑定。

限制条件

@Link装饰器不能在@Entry装饰的自定义组件中使用。

观察变化

  • 当装饰的数据类型为boolean、string、number类型时,可以同步观察到数值的变化。
  • 当装饰的数据类型为class或者Object时,可以观察到赋值和属性赋值的变化。
  • 当装饰的数据类型为array时,可以观察到数组的添加、删除、更新数据项的变化。
  • 当装饰的对象是Date时,可以观察到Date整体的赋值,同时可通过调用Date的接口setFullYear, setMonth, setDate, setHours, setMinutes, setSeconds, setMilliseconds, setTime, setUTCFullYear, setUTCMonth, setUTCDate, setUTCHours, setUTCMinutes, setUTCSeconds, setUTCMilliseconds 更新Date的属性。
  • 当装饰的变量是Map时,可以观察到Map整体的赋值,同时可通过调用Map的接口set, clear, delete 更新Map的值。
  • 当装饰的变量是Set时,可以观察到Set整体的赋值,同时可通过调用Set的接口add, clear, delete 更新Set的值。

框架行为

@Link装饰的变量和其所属的自定义组件共享生命周期。其初始渲染和双向更新流程如下

  1. 初始渲染:执行父组件的build()函数后将创建子组件的新实例。
    • 必须指定父组件中的@State变量,用于初始化子组件的@Link变量。子组件的@Link变量值与其父组件的数据源变量保持同步。
    • 父组件的@State状态变量包装类通过构造函数传递给子组件,子组件的@Link包装类拿到父组件的@State的状态变量后,将当前@Link包装类this指针注册给父组件的@State变量。
  2. @Link的数据源更新:即父组件中状态变量更新,引起相关子组件的@Link的更新。
    • 通过初始渲染的步骤可知,子组件@Link包装类把当前this指针注册给父组件。父组件@State变量变更后,会遍历更新所有依赖它的系统组件和状态变量。
    • 通知@Link包装类更新后,子组件中所有依赖@Link状态变量的系统组件都会被通知更新,以此来实现父组件对子组件的状态数据同步。
  3. @Link更新:当子组件中@Link更新后。
    • @Link更新后,调用父组件的@State包装类的set方法,将更新后的数值同步回父组件。
    • 子组件@Link和父组件@State分别遍历依赖的系统组件,进行对应的UI更新。以此来实现子组件@Link同步回父组件@State。

使用双向同步机制更改本地其他变量

使用@Watch可以在双向同步时,更改本地变量。

@Entry
@Component
struct Parent {
  @State sourceNumber: number = 0;

  build() {
    Column() {
      Text(`父组件的sourceNumber:` + this.sourceNumber)
      Child({ sourceNumber: this.sourceNumber })
      Button('父组件更改sourceNumber')
        .onClick(() => {
          this.sourceNumber++;
        })
    }
    .width('100%')
    .height('100%')
  }
}

@Component
struct Child {
  @State memberMessage: string = 'Hello World';
  @Link @Watch('onSourceChange') sourceNumber: number;

  onSourceChange() {
    this.memberMessage = this.sourceNumber.toString();
  }

  build() {
    Column() {
      Text(this.memberMessage)
      Text(`子组件的sourceNumber:` + this.sourceNumber.toString())
      Button('子组件更改memberMessage')
        .onClick(() => {
          this.memberMessage = 'Hello memberMessage';
        })
    }
  }
}

上述例子中,修改子组件中的@State变量,只会刷新子组件,并不会影响到父组件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值