深入解析鸿蒙 ArkTS 中的 @Local 装饰器

目录

前言

1.什么是@Local

2.@Local的特点与优势

1.私有状态

2.响应式

3.与V2生命周期兼容

4.类型安全

3.详细用法

[1. boolean、string、number](#1. boolean、string、number)

[2. 类对象](#2. 类对象)

3.单类型数组

4.嵌套类或对象数组

5.内置类型


前言

在鸿蒙 ArkTS 开发中,组件的状态管理是核心能力之一。随着 ArkTS 0.8+ 版本的演进,V2 组件体系引入了新的状态管理装饰器,其中 @Local 是最常用的一种。本文将全面介绍 @Local 的作用、使用方法、优势及实际案例。

这里需要说明的是:

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

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

1.什么是@Local

@Local 是 ArkTS V2 组件体系中用于声明组件内部私有状态的装饰器。它类似于V1的 @State,但在V2中功能更精细,性能更高。@Local 声明的状态只属于当前组件,外部无法直接访问或修改,保证了组件内部状态的封装性和独立性。

使用@Local 非常简单,你只需要在组件的类成员上加上装饰器即可:

复制代码
@ComponentV2
struct CounterComponent {
  @Local count: number = 0; // 内部状态

  build() {
    Column() {
      Text(`当前计数:${this.count}`)
        .fontSize(24)
        .padding(20)
      Row() {
        Button('增加')
          .onClick(() => { this.count++ })
        Button('减少')
          .onClick(() => { this.count-- })
      }.justifyContent(FlexAlign.SpaceAround)
    }
  }
}

在上面的代码中:

@Local count: number = 0:定义组件的私有状态count,初始值为0。

当count 发生变化时,组件会自动重新渲染。

外部无法直接访问 count,保证状态的封装性。

2.@Local的特点与优势

1.私有状态

@Local声明的状态仅属于当前组件,保证了组件内部的状态封装。其他组件或父组件无法直接访问或修改它。

2.响应式

组件会自动监听@Local 修饰的状态,当状态发生变化时,组件会触发重新渲染,无需手动刷新。

3.与V2生命周期兼容

@Local与V2组件的生命周期和性能优化机制兼容,支持冻结非活跃组件的状态监听,提高性能。

4.类型安全

使用TypeScript 的类型系统,@Local 支持类型推导和静态检查,减少运行时错误。

3.详细用法

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

1. boolean、string、number

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

复制代码
@ComponentV2
struct TodoListComponent {
  @Local todos: string[] = []

  addTodo(todo: string) {
    this.todos = [...this.todos, todo]; // 替换式赋值,触发渲染
  }

  build() {
    Column() {
      ForEach(this.todos, (item, index) => {
        ListItem() {
          Text(item)
        }
      }, item => item)
      Button('添加任务').onClick(() => this.addTodo('新任务'))
    }
  }
}

2. 类对象

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

如果我们定义了一个RawObject对象:

TypeScript 复制代码
class RawObject {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
}
@ObservedV2
class ObservedObject {
  @Trace name: string;
  constructor(name: string) {
    this.name = name;
  }
}

@Entry
@ComponentV2
struct LocalExample01 {

  @Local rawObject: RawObject = new RawObject("rawObject");
  @Local observedObject: ObservedObject = new ObservedObject("observedObject");

  build() {
    NavDestination(){
      Column() {
        Row(){
          Column(){
            Text(`${this.rawObject.name}`).margin({top:20})
            Text(`${this.observedObject.name}`).margin({top:20})
            Button("change object")
              .onClick(() => {
                // 对类对象整体的修改均能观察到
                this.rawObject = new RawObject("new rawObject");
                this.observedObject = new ObservedObject("new observedObject");
              }).margin({top:20})
            Button("change name")
              .onClick(() => {
                // @Local不具备观察类对象属性的能力,因此对rawObject.name的修改无法观察到
                this.rawObject.name = "new rawObject name";
                // 由于ObservedObject的name属性被@Trace装饰,因此对observedObject.name的修改能被观察到
                this.observedObject.name = "new observedObject name";
              }).margin({top:20})
          }.backgroundColor(Color.White).justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center).margin({top:20,bottom:20})
        }.margin({top:20,left:20,right:20}).borderColor(Color.Gray).width('100%').borderRadius(5).backgroundColor(Color.White).justifyContent(FlexAlign.Center)

      }
      .padding({
        left: '12vp',
        right: '12vp',
        bottom: '24vp'
      })
      .backgroundColor('#F1F3F5')
      .width('100%')
      .height('100%')
    }.title("Local修饰类对象")
  }
}

3.单类型数组

当装饰简单类型数组时,可以观察到数组整体或数组项的变化。

以下面的代码为例,点击按钮之后,UI中始终数组元素的Text实时刷新。

TypeScript 复制代码
@Entry
@ComponentV2
struct LocalExample02 {
  @Local numArr: number[] = [1,2,3,4,5];  // 使用@Local装饰一维数组变量
  @Local dimensionTwo: number[][] = [[1,2,3],[4,5,6]]; // 使用@Local装饰二维数组变量

  build() {
    NavDestination(){
      Column() {
        Text(`${this.numArr[0]}`)
        Text(`${this.numArr[1]}`)
        Text(`${this.numArr[2]}`)
        Text(`${this.dimensionTwo[0][0]}`)
        Text(`${this.dimensionTwo[1][1]}`)
        Button("change array item") // 按钮1:修改数组中的特定元素
          .onClick(() => {
            this.numArr[0]++;
            this.numArr[1] += 2;
            this.dimensionTwo[0][0] = 0;
            this.dimensionTwo[1][1] = 0;
          })
        Button("change whole array") // 按钮2:替换整个数组
          .onClick(() => {
            this.numArr = [5,4,3,2,1];
            this.dimensionTwo = [[7,8,9],[0,1,2]];
          })
      }.justifyContent(FlexAlign.SpaceEvenly).width('100%').height('100%')
    }.title("简单的数组")
  }
}

4.嵌套类或对象数组

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

5.内置类型

当装饰内置类型时,可以观察到变量整体赋值及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