状态管理V2

2025-03-26 19:56:27
139次阅读
0个评论

关注公众号“极客马拉松”, 发送“仓库”。 获取相关代码 @ObservedV2和@Trace 为了增强状态管理框架对类对象中属性的观测能力,开发者可以使用@ObservedV2装饰器和@Trace装饰器装饰类以及类中的属性。

使用@ObservedV2装饰的类中被@Trace装饰的属性具有被观测变化的能力,当该属性值变化时,会触发该属性绑定的UI组件刷新。

在嵌套类中使用@Trace装饰的属性具有被观测变化的能力。

class Son {
  @Trace age: number = 100;
}
class Father {
  son: Son = new Son();
}
@Entry
@ComponentV2
struct Index {
  father: Father = new Father();

  build() {
    Column() {
      // 当点击改变age时,Text组件会刷新
      Text(`${this.father.son.age}`)
        .onClick(() => {
          this.father.son.age++;
        })
    }
  }
}

在继承类中使用@Trace装饰的属性具有被观测变化的能力。

class Father {
  @Trace name: string = "Tom";
}
class Son extends Father {
}
@Entry
@ComponentV2
struct Index {
  son: Son = new Son();

  build() {
    Column() {
      // 当点击改变name时,Text组件会刷新
      Text(`${this.son.name}`)
        .onClick(() => {
          this.son.name = "Jack";
        })
    }
  }
}

类中使用@Trace装饰的静态属性具有被观测变化的能力。

class Manager {
  @Trace static count: number = 1;
}
@Entry
@ComponentV2
struct Index {
  build() {
    Column() {
      // 当点击改变count时,Text组件会刷新
      Text(`${Manager.count}`)
        .onClick(() => {
          Manager.count++;
        })
    }
  }
}

@ComponentV2 为了在自定义组件中使用V2版本状态变量装饰器的能力,开发者可以使用@ComponentV2装饰器装饰自定义组件。 ● 在@ComponentV2装饰的自定义组件中,开发者仅可以使用全新的状态变量装饰器,包括@Local、@Param、@Once、@Event、@Provider、@Consumer等。 ● @ComponentV2装饰的自定义组件暂不支持组件复用、LocalStorage等现有自定义组件的能力。 ● 无法同时使用@ComponentV2与@Component装饰同一个struct结构。 ● @ComponentV2支持一个可选的boolean类型参数freezeWhenInactive,来实现组件冻结功能。

@Local @Local表示组件内部的状态,使得自定义组件内部的变量具有观测变化的能力: ● 被@Local装饰的变量无法从外部初始化,因此必须在组件内部进行初始化。 ● 当被@Local装饰的变量变化时,会刷新使用该变量的组件。 ● @Local支持观测number、boolean、string、Object、class等基本类型以及Array、Set、Map、Date等内嵌类型。 ● @Local的观测能力仅限于被装饰的变量本身。当装饰简单类型时,能够观测到对变量的赋值;当装饰对象类型时,仅能观测到对对象整体的赋值;当装饰数组类型时,能观测到数组整体以及数组元素项的变化;当装饰Array、Set、Map、Date等内嵌类型时,可以观测到通过API调用带来的变化。详见观察变化。 ● @Local支持null、undefined以及联合类型。 注意,@Local无法和@Observed装饰的类实例对象混用。

  name: string;
  constructor(name: string) {
    this.name = name;
  }
}
@ObservedV2
class ObservedObject {
  @Trace name: string;
  constructor(name: string) {
    this.name = name;
  }
}
@Entry
@ComponentV2
struct Index {
  @Local rawObject: RawObject = new RawObject("rawObject");
  @Local observedObject: ObservedObject = new ObservedObject("observedObject");
  build() {
    Column() {
      Text(`${this.rawObject.name}`)
      Text(`${this.observedObject.name}`)
      Button("change object")
        .onClick(() => {
          // 对类对象整体的修改均能观察到
          this.rawObject = new RawObject("new rawObject");
          this.observedObject = new ObservedObject("new observedObject");
      })
      Button("change name")
        .onClick(() => {
          // @Local不具备观察类对象属性的能力,因此对rawObject.name的修改无法观察到
          this.rawObject.name = "new rawObject name";
          // 由于ObservedObject的name属性被@Trace装饰,因此对observedObject.name的修改能被观察到
          this.observedObject.name = "new observedObject name";
      })
    }
  }
}

@Param ● @Param装饰的变量支持本地初始化,但是不允许在组件内部直接修改变量本身。 ● 被@Param装饰的变量能够在初始化自定义组件时从外部传入,当数据源也是状态变量时,数据源的修改会同步给@Param。 ● @Param可以接受任意类型的数据源,包括普通变量、状态变量、常量、函数返回值等。 ● @Param装饰的变量变化时,会刷新该变量关联的组件。 ● @Param支持观测number、boolean、string、Object、class等基本类型以及Array、Set、Map、Date等内嵌类型。 ● 对于复杂类型如类对象,@Param会接受数据源的引用。在组件内可以修改类对象中的属性,该修改会同步到数据源。

class Info {
  @Trace name: string;
  constructor(name: string) {
    this.name = name;
  }
}
@Entry
@ComponentV2
struct Index {
  @Local info: Info = new Info("Tom");
  build() {
    Column() {
      Text(`Parent info.name ${this.info.name}`)
      Button("Parent change info")
        .onClick(() => {
          this.info = new Info("Lucy"); // 父组件更改@Local变量,会同步子组件对应@Param变量
      })
      Child({ info: this.info })
    }
  }
}
@ComponentV2
struct Child {
  @Require @Param info: Info;
  build() {
    Column() {
      Text(`info.name: ${this.info.name}`)
      Button("change info")
        .onClick(() => {
          this.info = new Info("Jack"); //错误用法,不允许在子组件更改@Param变量,编译时报错
        })
      Button("Child change info.name")
        .onClick(() => {
          this.info.name = "Jack"; // 允许在子组件中更改对象中属性
        })
    }
  }
}

@Once 为了实现仅从外部初始化一次、不接受后续同步变化的能力,开发者可以使用@Once装饰器搭配@Param装饰器使用。

@Once装饰器仅在变量初始化时接受外部传入值进行初始化,当后续数据源更改时,不会将修改同步给子组件: ● @Once必须搭配@Param使用,单独使用或搭配其他装饰器使用都是不允许的。 ● @Once不影响@Param的观测能力,仅针对数据源的变化做拦截。 ● @Once与@Param装饰变量的先后顺序不影响实际功能。 ● @Once与@Param搭配使用时,可以在本地修改@Param变量的值。 当@Once搭配@Param使用时,可以解除@Param无法在本地修改的限制,且修改能够触发UI刷新。此时,使用@Param @Once相当于使用@Local,区别在于@Param @Once能够接受外部传入初始化。

@Event 为了实现子组件向父组件要求更新@Param装饰变量的能力,开发者可以使用@Event装饰器。使用@Event装饰回调方法是一种规范,表明子组件需要传入更新数据源的回调 @Event主要配合@Param实现数据的双向同步。 ● @Event装饰的回调方法中参数以及返回值由开发者决定。 ● @Event装饰非回调类型的变量不会生效。当@Event没有初始化时,会自动生成一个空的函数作为默认回调。 ● 当@Event未被外部初始化,但本地有默认值时,会使用本地默认的函数进行处理。

使用@Event可以更改父组件中变量,当该变量作为子组件@Param变量的数据源时,该变化会同步回子组件的@Param变量。使用@Event修改父组件的值是立刻生效的,但从父组件将变化同步回子组件的过程是异步的,即在调用完@Event的方法后,子组件内的值不会立刻变化。这是因为@Event将子组件值实际的变化能力交由父组件处理,在父组件实际决定如何处理后,将最终值在渲染之前同步回子组件。

@Provider & @Consumer @Provider和@Consumer用于跨组件层级数据双向同步,可以使得开发者不用拘泥于组件层级。 @Provider,即数据提供方,其所有的子组件都可以通过@Consumer绑定相同的key来获取@Provider提供的数据。 @Consumer,即数据消费方,可以通过绑定同样的key获取其最近父节点的@Provider的数据,当查找不到@Provider的数据时,使用本地默认值。 开发者在使用@Provider和@Consumer时要注意: ● @Provider和@Consumer强依赖自定义组件层级,@Consumer会因为所在组件的父组件不同,而被初始化为不同的值。 ● @Provider和@Consumer相当于把组件粘合在一起了,从组件独立角度,要减少使用@Provider和@Consumer。

@Monitor 为了增强状态管理框架对状态变量变化的监听能力,开发者可以使用@Monitor装饰器对状态变量进行监听。 Monitor只能监听被@Local、@Param、@Provider、@Consumer、@Computed装饰的变量。 同时可以监听多个变量的变化,并能记录变化前和变化后的值。

被@Trace和@ObserveredV2装饰的类不能和V1混用。@Trace装饰器与现有状态管理框架的@Track与@State装饰器的能力不同,@Track使class具有属性级更新的能力,但并不具备深度观测的能力;而@State只能观测到对象本身以及第一层的变化,对于多层嵌套场景只能通过封装自定义组件,搭配@Observed和@ObjectLink来实现观测。

收藏00

登录 后评论。没有帐号? 注册 一个。