1
头图

简单介绍一下关于ArkUI中的状态管理组件:
@State :用于定义组件内部状态,当状态发生变化时,组件会自动重新渲染,以反映最新的状态值,适用于组件内部的状态管理

特点 :

  • 私有性 :只能在所属组件内部访问。
  • 自动更新 :状态变量变化时,相关 UI 组件会同步更新。
  • 生命周期绑定 :生命周期与组件相同,组件销毁后状态失效
  • 支持的数据类型 :包括简单类型(string/number/boolean)、复杂类型(Object/Array/Date/Map/Set)、联合类型及 ArkUI 框架类型(如 ResourceStr)等。
  • 观察机制 :能观测到第一层属性变化,深层嵌套需通过 @Observed 装饰器增强
  • 数据同步 :与子组件中的 @Prop 装饰变量建立单向数据同步,与 @Link、@ObjectLink 装饰变量建立双向数据同步。
  • 示例 :
@Component
struct MyComponent {
    @State count: number = 0;

    build() {
        Button(`Clicked ${this.count} times`)
            .onClick(() => { this.count++ })
    }
}

装饰简单类型的变量 :当 count 改变时,只有关联它的 Button 组件会刷新:

@Entry
@Component
struct MyComponent {
  @State count: number = 0;
  build() {
    Button(`click times: ${this.count}`)
      .onClick(() => {
        this.count += 1;
      })
  }
}

装饰 class 对象类型的变量 :如果 count 或 title 的值发生变化,会查询 MyComponent 中使用该状态变量的 UI 组件并重新渲染

class Model {
  public value: string;
  constructor(value: string) {
    this.value = value;
  }
}
@Entry
@Component
struct EntryComponent {
  build() {
    Column() {
      MyComponent({ count: 1 })
        .width(300)
      MyComponent({ title: new Model('Hello World 2'), count: 7 })
    }
  }
}
@Component
struct MyComponent {
  @State title: Model = new Model('Hello World');
  @State count: number = 0;
  private increaseBy: number = 1;
  build() {
    Column() {
      Text(`${this.title.value}`)
        .margin(10)
      Button(`Click to change title`)
        .onClick(() => {
          this.title.value = this.title.value === 'Hello ArkUI' ? 'Hello World' : 'Hello ArkUI';
        })
        .width(300)
        .margin(10)
      Button(`Click to increase count = ${this.count}`)
        .onClick(() => {
          this.count += this.increaseBy;
        })
        .width(300)
        .margin(10)
    }
  }
}

装饰器使用规则

  • 无参数 :@State 装饰器无参数。
  • 同步类型 :不与父组件中任何类型的变量同步。
  • 允许装饰的变量类型 :支持 Object、class、string、number、boolean、enum 类型以及这些类型的数组;支持 Date 类型;API 11 及以上支持 Map、Set 类型;支持 undefined 和 null 类型(建议显式指定类型);支持 ArkUI 框架定义的联合类型,如 Length、ResourceStr、ResourceColor 等;API 11 及以上支持上述支持类型的联合类型,如 string | number 等,不支持 any 类型。
  • 初始值 :必须本地初始化,也可选择使用命名参数机制从父组件完成初始化,若从父组件初始化将会覆盖本地初始化

变量的传递 / 访问规则:

  • 从父组件初始化 :支持父组件中常规变量、@State、@Link、@Prop、@Provide、@Consume、@ObjectLink、@StorageLink、@StorageProp、@LocalStorageLink 和 @LocalStorageProp 装饰的变量来初始化子组件的 @State。
  • 用于初始化子组件 :@State 装饰的变量支持初始化子组件的常规变量、@State、@Link、@Prop、@Provide。
  • 组件外访问 :不支持组件外访问,只能在组件内访问

@Prop :用于从父组件向子组件传递数据,子组件可以通过 @Prop 装饰器接收父组件传递的属性值,并在自身内部使用
特点 :

  • 单向同步 :父组件状态变化会同步到子组件,但子组件修改不会同步回父组件。
  • 初始化 :可在声明时本地初始化,也可从父组件初始化。
  • 类型限制 :必须有明确的类型声明

使用方法

  • 子组件中使用 @Prop 定义变量 :在子组件中使用 @Prop 装饰器来定义一个变量,该变量将用于接收父组件传递的数据。
  • 父组件中传递数据 :在父组件中创建子组件实例时,通过对象字面量的方式将数据传递给子组件的 @Prop 变量。
  • 示例 :

父组件 :

@Component
struct ParentComponent {
    @State value: string = 'default';

    build() {
        ChildComponent({ value: this.value });
    }
}

子组件:

@Component
struct ChildComponent {
    @Prop value: string;

    build() {
        // UI构建逻辑
    }
}

注意事项

  • 类型一致性 :@Prop 装饰的变量与父组件的状态变量类型需要保持一致。
  • 不可在 @Entry 组件中使用 :@Prop 装饰器不能在 @Entry 装饰的自定义组件中使用。
  • 数据覆盖 :当父组件的数据源更新时,子组件的 @Prop 变量将被来自父组件的数据源重置,所有本地的修改都将被覆盖

@Link :实现父子组件间的双向绑定,当子组件的值发生变化时,父组件中的对应数据也会自动更新,反之亦然,常用于表单组件等需要双向数据绑定的场景。
特点 :

  • 双向同步 :父组件状态变化会同步到子组件,子组件对状态的更新也会同步到父组件。
  • 无初始值 :被 @Link 修饰的属性不允许有初始值,必须由父组件的状态属性进行初始化。
  • 使用限制 :不允许在页面入口组件(被 @Entry 修饰的组件)中使用。
  • 示例 :

父组件 :

@Entry
@Component
struct ParentComponent {
  @State count: number = 0;

  build() {
    Column() {
      Button('Increment in Parent')
        .onClick(() => { this.count++; })
        .margin({ bottom: 10 })
      // 将状态传递给子组件
      ChildComponent({ count: this.count })
    }
  }
}

子组件:

@Component
struct ChildComponent {
  @Link count: number;

  build() {
    Column() {
      Text(`Count from Parent: ${this.count}`)
        .fontSize(20)
        .margin({ bottom: 10 })
      // 在子组件中修改状态,同步回父组件
      Button('Increment in Child')
        .onClick(() => { this.count++; })
    }
  }
}

@Observed :用于监听对象的变化,当被监听的对象发生改变时,组件会自动更新 UI,适用于复杂的数据结构和对象状态管理。
特点 :

  • 单独使用无作用,需和 @ObjectLink、@Prop 连用。
  • 示例 :
    父组件 :
@Entry
@Component
struct ParentComponent {
    @State user: User = new User()

    build() {
        Column() {
            ChildComponent(this.user)
        }
    }
}

class User {
    name: string = 'John'
    address: Address = new Address()
}

class Address {
    city: string = 'New York'
}

子组件:

@Component
struct ChildComponent {
    @Prop user: User

    @Builder
    buildAddress() {
        if (this.user.address.city === 'New York') {
            Text('City: New York')
        }
    }

    build() {
        Column() {
            Text(`Name: ${this.user.name}`)
            this.buildAddress()
        }
    }
}

使用 @Observed@ObjectLink 的子组件:

@Component
struct ObservedChildComponent {
    @ObjectLink user: User

    @Builder
    buildAddress() {
        if (this.user.address.city === 'New York') {
            Text('City: New York')
        }
    }

    build() {
        Column() {
            Text(`Name: ${this.user.name}`)
            this.buildAddress()
        }
    }
}

@ObjectLink :用于更复杂的数据绑定场景,可实现对象的深度绑定,当对象的任意属性发生变化时,组件都能及时感知并更新
特点 :

  • 与 @Observed 配合使用,可实现对复杂数据结构的深度观测和同步。
  • 示例 :见上述 @Observed 示例中的子组件 ObservedChildComponent,其中 @ObjectLink user: User 接收父组件中 @Observed 装饰的 User 类实例,并与其构建双向同步关系

使用步骤

  1. 父组件中定义类并使用 @Observed 装饰 :先定义一个类,并在类前使用 @Observed 装饰器,表明该类的实例能被观察到属性变化。然后在父组件中创建该类的实例。
  2. 子组件中使用 @ObjectLink 接收实例 :在子组件中,通过 @ObjectLink 装饰器声明一个变量,用于接收父组件传递的 @Observed 类的实例。
  3. 父组件将类实例传递给子组件 :在父组件中创建子组件实例时,将 @Observed 类的实例作为参数传递给子组件
  • 示例
    定义类 :
@Observed
class Pet {
  petName: string;
  petAge: number;

  constructor(name: string, age: number) {
    this.petName = name;
    this.petAge = age;
  }
}

父组件 :

@Entry
@Component
struct ParentComponent {
  @State pet: Pet = new Pet("Dog", 2);

  build() {
    Column() {
      Button("Change Pet Age")
        .onClick(() => {
          this.pet.petAge++;
        })
      PetInfo({ pet: this.pet });
    }
  }
}

子组件 :

@Component
struct PetInfo {
  @ObjectLink pet: Pet;

  build() {
    Row() {
      Text(`Pet Name: ${this.pet.petName}, Age: ${this.pet.petAge}`)
        .fontSize(20)
      Button("Update Pet Name")
        .onClick(() => {
          this.pet.petName = "Cat";
        })
    }
  }
}

注意事项

  • 配合 @Observed 使用 :@ObjectLink 必须与 @Observed 配合使用,装饰的变量类型必须为被 @Observed 装饰的类实例。
  • 变量不可重新赋值 :@ObjectLink 装饰的对象变量本身不可重新赋值,但可以修改对象内部的属性值。
  • 初始化要求 :必须让父组件中有一个由 @State、@Link、@StorageLink、@Provide 或 @Consume 所装饰变量参与的 TS 表达式进行初始化

画楼西畔
6.3k 声望963 粉丝