鸿蒙中级课程笔记2—状态管理V2—@Local

@Local装饰器:组件内部状态

为了实现对@ComponentV2装饰的自定义组件中变量变化的观测,开发者可以使用@Local装饰器装饰变量。

在阅读本文档前,建议提前阅读:@ComponentV2。常见问题请参考组件内状态变量常见问题

说明

从API version 12开始,在@ComponentV2装饰的自定义组件中支持使用@Local装饰器。

从API version 12开始,该装饰器支持在元服务中使用。

概述

@Local表示组件内部的状态,使得自定义组件内部的变量具有观察变化的能力:

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

  • 当被@Local装饰的变量变化时,会刷新使用该变量的组件。

  • @Local支持观测number、boolean、string、Object、class等基本类型以及ArraySetMapDate等内嵌类型。

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

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

状态管理V1版本@State装饰器的局限性

状态管理V1使用@State装饰器定义组件中的基础状态变量,该状态变量常用来作为组件内部状态,在组件内使用。但由于@State装饰器又能够从外部初始化,因此无法确保@State装饰变量的初始值一定为组件内部定义的值。

TypeScript 复制代码
class ComponentInfo {
  public name: string;
  public count: number;
  public message: string;

  constructor(name: string, count: number, message: string) {
    this.name = name;
    this.count = count;
    this.message = message;
  }
}

@Component
struct Child {
  @State componentInfo: ComponentInfo = new ComponentInfo('Child', 1, 'Hello World'); // 父组件传递的componentInfo会覆盖初始值

  build() {
    Column() {
      Text(`componentInfo.message is ${this.componentInfo.message}`)
    }
  }
}

@Entry
@Component
struct Index {
  build() {
    Column() {
      Child({ componentInfo: new ComponentInfo('Unknown', 0, 'Error') })
    }
  }
}

上述代码中,可以通过在初始化Child自定义组件时传入新的值来覆盖作为内部状态变量使用的componentInfo。但Child自定义组件并不能感知到componentInfo从外部进行了初始化,这不利于自定义组件内部状态的管理。因此推出@Local装饰器表示组件的内部状态。

@Local装饰器说明

@Local变量装饰器 说明
装饰器参数 无。
可装饰的变量类型 Object、class、string、number、boolean、enum等基本类型以及Array、Date、Map、Set等内嵌类型。支持null、undefined以及联合类型。
装饰变量的初始值 必须本地初始化,不允许外部传入初始化。

@Local装饰变量传递

传递规则 说明
从父组件初始化 @Local装饰的变量仅允许本地初始化,无法从外部传入初始化。
初始化子组件 @Local装饰的变量可以初始化子组件中@Param装饰的变量。

观察@Local装饰变量变化

使用@Local装饰的变量具有观察变化的能力。当装饰的变量发生变化时,会触发该变量绑定的UI组件刷新。

  • 当装饰的变量类型为boolean、string、number时,可以观察到对变量赋值的变化。

  • 当@Local装饰的变量类型为null、undefined以及联合类型,变量类型改变后,UI会随之刷新。如count类型为number | undefined,初始count值为number类型,将count值变为undefined类型,UI会随之刷新。

  • 当装饰的变量类型为类对象时,仅可以观察到对类对象整体赋值的变化,无法直接观察到对类成员属性赋值的变化,对类成员属性的观察依赖@ObservedV2@Trace装饰器。注意,API version 19之前,@Local无法和@Observed装饰的类实例对象混用。API version 19及以后,支持部分状态管理V1V2混用能力,允许@Local和@Observed同时使用,详情见状态管理V1V2混用文档

    TypeScript 复制代码
    class RawObject {
      public name: string;
    
      constructor(name: string) {
        this.name = name;
      }
    }
    
    @ObservedV2
    class ObservedObject {
      @Trace public 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';
            })
        }
      }
    }
  • 当装饰简单类型数组时,可以观察到数组整体或数组项的变化。

  • 当装饰的变量是嵌套类或对象数组时,@Local无法观察深层对象属性的变化。对深层对象属性的观测依赖@ObservedV2与@Trace装饰器。

  • 当装饰内置类型时,可以观察到变量整体赋值及API调用带来的变化。

    类型 可观察变化的API
    Array push, pop, shift, unshift, splice, copyWithin, fill, reverse, sort
    Date setFullYear, setMonth, setDate, setHours, setMinutes, setSeconds, setMilliseconds, setTime, setUTCFullYear, setUTCMonth, setUTCDate, setUTCHours, setUTCMinutes, setUTCSeconds, setUTCMilliseconds
    Map set, clear, delete
    Set add, clear, delete

@Local使用限制

@Local装饰器存在以下使用限制:

  • @Local装饰器只能在@ComponentV2装饰的自定义组件中使用,否则编译时报错。

  • @Local装饰的变量表示组件内部状态,不允许从外部传入初始化,否则编译时报错。

@Local与@State对比

@Local与@State的用法、功能对比如下:

用法 @State @Local
参数 无。 无。
从父组件初始化 可选。 不允许外部初始化。
观察能力 能观测变量本身以及一层的成员属性,无法深度观测。 能观测变量本身,深度观测依赖@Trace装饰器。
数据传递 可以作为数据源和子组件中状态变量同步。 可以作为数据源和子组件中状态变量同步。

使用场景

观测对象整体变化

被@ObservedV2与@Trace装饰的类对象实例,具有深度观测对象属性的能力。使用@Local装饰对象,可以达到观测对象本身变化的效果。例子参考华为中级课程------@Local------观测对象整体变化

装饰Array类型变量

当装饰的对象是Array时,可以观察到Array整体的赋值,同时可以通过调用Array的接口push, pop, shift, unshift, splice, copyWithin, fill, reverse, sort更新Array中的数据。同时,数组中某个元素的直接赋值也可以监测到。华为中级课程------@Local------装饰Array类型变量

装饰Date类型变量

当装饰的对象是Date时,可以观察到Date整体的赋值,同时可通过调用Date的接口setFullYear, setMonth, setDate, setHours, setMinutes, setSeconds, setMilliseconds, setTime, setUTCFullYear, setUTCMonth, setUTCDate, setUTCHours, setUTCMinutes, setUTCSeconds, setUTCMilliseconds更新Date的属性。华为中级课程------@Local------装饰Date类型变量

装饰Map类型变量

当装饰的对象是Map时,可以观察到对Map整体的赋值,同时可以通过调用Map的接口set, clear, delete更新Map中的数据。华为中级课程------@Local------装饰Map类型变量

装饰Set类型变量

当装饰的对象是Set时,可以观察到对Set整体的赋值,同时可以通过调用Set的接口add, clear, delete更新Set中的数据。华为中级课程------@Local------装饰Set类型变量

联合类型

@Local支持null、undefined以及联合类型。在下面的示例中,count类型为number | undefined,初始count值为number类型,将count值变为undefined类型,UI会随之刷新。华为中级课程------@Local------联合类型

复制代码

常见问题

在状态管理V2中使用animateTo动画效果异常

animateTo暂不支持直接在状态管理V2中动画前改变值。

TypeScript 复制代码
@Entry
@ComponentV2
struct Index {
  @Local w: number = 50; // 宽度
  @Local h: number = 50; // 高度
  @Local message: string = 'Hello';

  build() {
    Column() {
      Button('change size')
        .margin(20)
        .onClick(() => {
          // 在执行动画前,存在额外的修改
          this.w = 100;
          this.h = 100;
          this.message = 'Hello World';
          this.getUIContext().animateTo({
            duration: 1000
          }, () => {
            this.w = 200;
            this.h = 200;
            this.message = 'Hello ArkUI';
          })
        })
      Column() {
        Text(`${this.message}`)
      }
      .backgroundColor('#ff17a98d')
      .width(this.w)
      .height(this.h)
    }
  }
}

上述代码中,开发者预期的动画效果是:绿色矩形从长宽100变为200,字符串从Hello World变为Hello ArkUI。但由于当前animateTo与V2的刷新机制不兼容,执行动画前的额外修改未生效,实际显示的动画效果是:绿色矩形从长宽50变为200,字符串从Hello变为Hello ArkUI。

从API version 22开始,可以使用applySync接口实现预期的显示效果。

TypeScript 复制代码
import { UIUtils } from '@kit.ArkUI';

@Entry
@ComponentV2
struct Index {
  @Local w: number = 50; // 宽度
  @Local h: number = 50; // 高度
  @Local message: string = 'Hello';

  build() {
    Column() {
      Button('change size')
        .margin(20)
        .onClick(() => {
          // 在执行动画前,存在额外的修改
          UIUtils.applySync(() => {
            this.w = 100;
            this.h = 100;
            this.message = 'Hello World';
          })
          this.getUIContext().animateTo({
            duration: 1000
          }, () => {
            this.w = 200;
            this.h = 200;
            this.message = 'Hello ArkUI';
          })
        })
      Column() {
        Text(`${this.message}`)
      }
      .backgroundColor('#ff17a98d')
      .width(this.w)
      .height(this.h)
    }
  }
}

原理为使用applySync接口同步刷新闭包函数内的状态变量变化,再执行原来的动画达成预期的效果。

相关推荐
巴德鸟4 分钟前
华为手机鸿蒙4回退到鸿蒙3到鸿蒙2再回退到EMUI11 最后关闭系统更新
华为·智能手机·harmonyos·降级·升级·回退·emui
一起养小猫6 分钟前
Flutter for OpenHarmony 实战_魔方应用UI设计与交互优化
flutter·ui·交互·harmonyos
三水不滴22 分钟前
计网ping原理
经验分享·笔记·计算机网络
一只大侠的侠29 分钟前
Flutter开源鸿蒙跨平台训练营 Day7Flutter+ArkTS双方案实现轮播图+搜索框+导航组件
flutter·开源·harmonyos
那就回到过去31 分钟前
VRRP协议
网络·华为·智能路由器·ensp·vrrp协议·网络hcip
prog_610338 分钟前
【笔记】思路分享:各种大模型免费当agent后台
笔记·大语言模型·agent·cursor
凯尔萨厮1 小时前
Maven(Windows下载安装)
笔记·maven
wdfk_prog1 小时前
[Linux]学习笔记系列 -- [drivers][input]serio
linux·笔记·学习
相思难忘成疾1 小时前
通向HCIP之路:第四步:边界网关路由协议—BGP(概念、配置、特点、常见问题及其解决方案)
网络·华为·hcip
一只大侠的侠1 小时前
Flutter开源鸿蒙跨平台训练营 Day9分类数据的获取与渲染实现
flutter·开源·harmonyos