状态管理
鸿蒙状态库V1和V2都提供了一系列装饰器用于状态管理,V2在V1的基础上进行了优化和升级,功能更加强大。以下是详细汇总:
鸿蒙状态库V1
- 核心装饰器:
- @State:用于声明组件的状态变量,当状态变化时,相关UI会自动更新。但跨组件共享状态不够方便。
- @Prop:用于父子组件传值,实现单向数据传递。
- @Link:实现父子组件状态双向绑定。
- @Provide和**@Consume**:用于跨组件共享状态,但层级较深时维护起来比较麻烦。
- @Observed:用于观测对象的变化,但观测深度有限,无法直接观测嵌套类属性的变化。
- 示例:
- @State示例:
@Entry
@Component
struct StateExample {
@State count: number = 0;
build() {
Column() {
Text(`计数: ${this.count}`)
Button('增加计数')
.onClick(() => {
this.count++;
})
}
}
}
- @Prop示例:
@Component
struct ParentComponent {
@State message: string = 'Hello, Child!';
build() {
Column() {
ChildComponent({ propMessage: this.message })
}
}
}
@Component
struct ChildComponent {
@Prop propMessage: string;
build() {
Text(this.propMessage)
}
}
- 注意点:
- 避免将未关联UI组件的普通变量声明为状态变量,以免增加状态管理开销。
- 不要在build()方法内直接修改状态变量,应通过事件回调修改,否则会触发额外渲染。
- 按共享范围选择最小化装饰器,避免过度使用全局状态导致不必要的组件刷新。
- 拆分高频变化属性,独立存储,防止单一状态对象中任一属性变化触发整个对象关联组件的刷新。
- 循环逻辑中避免直接读取状态变量,应用临时变量缓存状态值,减少性能劣化。
- 注册事件监听后,需在页面销毁时取消监听,防止内存泄漏。
- 连续多次修改状态变量时,用临时变量合并修改,单次赋值,避免多次UI刷新。
- 对于嵌套对象属性变更,可使用@ObjectLink装饰子组件属性或手动触发父对象更新,否则无法被观测。
- 避免直接操作状态管理框架返回的代理对象,可使用UIUtils.getTarget()获取原始对象,防止类型判断错误。
- 子线程更新状态后,需使用StateStore的子线程更新能力或TaskPool同步到主线程,避免UI数据不一致。
鸿蒙状态库V2
- 核心装饰器:
- @State:得到加强,不仅支持组件内部的状态管理,还支持跨组件共享状态。
- @Prop:支持双向绑定和单向绑定两种模式,父子传值更灵活。
- @Provide和**@Consumer**:支持任意组件之间的状态共享,状态共享更高效。
- @ObservedV2和**@Trace**:配合使用可实现跨嵌套层级观测,用于深度观测对象属性变化。
- @Local:用于管理组件内部状态,变量可从父组件传入,也可在本地初始化。
- 示例:
- @State示例:
@Entry
@ComponentV2
struct StateExampleV2 {
@State count: number = 0;
build() {
Column() {
Text(`计数: ${this.count}`)
AnotherComponent({ count: this.count })
Button('增加计数')
.onClick(() => {
this.count++;
})
}
}
}
@ComponentV2
struct AnotherComponent {
@Consume count: number;
build() {
Text(`另一个组件中计数: ${this.count}`)
}
}
- @ObservedV2和@Trace示例:
@ObservedV2
class Task {
@Trace isFinish: boolean = false;
taskName: string;
constructor(name: string) {
this.taskName = name;
}
}
@ComponentV2
struct TaskItem {
@Param task: Task;
build() {
Row() {
Checkbox(this.task.isFinish)
.onChange((value) => {
this.task.isFinish = value;
})
Text(this.task.taskName).decoration({ textDecoration: this.task.isFinish? TextDecoration.LineThrough : TextDecoration.None })
}
}
}
@Entry
@ComponentV2
struct TodoList {
@Local tasks: Task[] = [new Task('任务1'), new Task('任务2')];
build() {
Column() {
ForEach(this.tasks, (task) => {
TaskItem({ task: task })
}, task => task.taskName)
Button('全部完成')
.onClick(() => {
this.tasks.forEach(task => task.isFinish = true);
})
Button('全部未完成')
.onClick(() => {
this.tasks.forEach(task => task.isFinish = false);
})
}
}
}
- 注意点:
- @ObservedV2修饰的变量只能在本地初始化,不能由外部传入。
- 使用@Trace修饰的属性才能参与UI绘制,未修饰的属性修改不会引起UI刷新。
- 被@Param修饰的变量一般不能在组件内部直接修改,若要修改可配合@Once,但被@Once装饰的变量仅能在外部初始化一次,外部修改变量本身不会同步到子组件。
- 状态管理V2会给使用状态变量装饰器如@Trace、@Local装饰的Date、Map、Set、Array添加一层代理用于观测API调用产生的变化,取其中的值进行比较时,需使用UIUtils.getTarget()获取原始对象进行比较。