简单介绍一下关于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 类实例,并与其构建双向同步关系
使用步骤
- 父组件中定义类并使用 @Observed 装饰 :先定义一个类,并在类前使用 @Observed 装饰器,表明该类的实例能被观察到属性变化。然后在父组件中创建该类的实例。
- 子组件中使用 @ObjectLink 接收实例 :在子组件中,通过 @ObjectLink 装饰器声明一个变量,用于接收父组件传递的 @Observed 类的实例。
- 父组件将类实例传递给子组件 :在父组件中创建子组件实例时,将 @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 表达式进行初始化
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。