如果你刚接触 HarmonyOS ArkUI,第一次看到各种装饰器时可能会有这样的心情:
"哇,这么多 @ 注解,都是什么鬼?能不能不要这么复杂?"
别慌! 其实它们一点也不难。你把 UI 组件想象成一个个小房间,把状态装饰器想象成房间里的"家具"、"窗户"、"储物柜",就会发现它们非常好理解。
本文将用最轻松幽默的方式 ,把 状态管理 V1 和 V2 全系列装饰器一网打尽,帮你一次读懂!
一、为什么需要状态管理?
简单一句话:
状态管理决定你的 UI 长什么样、什么时候更新,以及组件之间怎么传递数据。
举个简单例子:
你有一个计数器按钮,点一下数字加一。数字就是 状态,只有状态变了,UI 才会更新。
所以我们需要告诉系统:
- 哪些变量是状态?
- 谁可以读?
- 谁可以改?
- 改了要不要通知别人?
- 能不能共享给其他组件?
这就是各种装饰器存在的意义!
二、状态管理 V1(旧体系)
HarmonyOS 早期的状态管理体系,主要包括:
@State@Prop@Link@Observed@ObjectLink@Provide / @Consume@Watch
我们用大白话讲,保证你马上懂。
1. @State ------ 我自己的状态,我自己改
组件内部自己维护、自己改动的数据。
就像房间里的闹钟,别人碰不到,只有我能调。
示例
typescript
@Entry
@Component
struct CounterPage {
@State count: number = 0;
build() {
Column() {
Button(`点击次数:${this.count}`)
.onClick(() => {
this.count++; // 自动触发 UI 更新
})
}
}
}
作用: 组件内部使用,修改后自动刷新 UI。
2. @Prop ------ 父给我的"只读礼物"
只能父组件给,子组件可以修改但不会影响父组件。
重要修正: @Prop 子组件可以修改,但修改只在子组件内生效,不会回传给父组件。
示例
父组件:
typescript
@Entry
@Component
struct Parent {
@State name: string = "张三";
build() {
Column() {
Text(`父组件:${this.name}`)
ChildComp({ name: this.name })
}
}
}
子组件:
typescript
@Component
struct ChildComp {
@Prop name: string;
build() {
Column() {
Text(`你好,${this.name}`)
Button('改名')
.onClick(() => {
this.name = "李四"; // 只在子组件生效
})
}
}
}
3. @Link ------ 父子共用一个状态(双向绑定)
父组件给子组件同一个变量的引用,谁改都会同步。
就像你和室友共用一个 WiFi 密码,谁改了密码,两个人一起受影响。
示例
父组件:
typescript
@Entry
@Component
struct Parent {
@State count: number = 0;
build() {
Column() {
Text(`父组件 count: ${this.count}`)
ChildComp({ count: this.count }) // 使用 $ 传递引用
}
}
}
子组件:
typescript
@Component
struct ChildComp {
@Link count: number;
build() {
Button(`子组件 count: ${this.count}`)
.onClick(() => this.count++)
}
}
父子都会同步 UI!
4. @Observed ------ 观察对象的属性变化
用于"对象类型"的监听,必须配合 @ObjectLink 使用才能让对象内部字段变化更新 UI。
重要修正: @Observed 是类装饰器,不是用在属性上。
示例
typescript
@Observed
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
@Entry
@Component
struct Parent {
@State person: Person = new Person("张三", 18);
build() {
Column() {
Text(`${this.person.name}, ${this.person.age}岁`)
ChildComp({ person: this.person })
}
}
}
5. @ObjectLink ------ 给子组件传"对象引用",对象属性变化会同步
必须与 @Observed 配合使用。
示例
子组件:
typescript
@Component
struct ChildComp {
@ObjectLink person: Person;
build() {
Button("长大一岁")
.onClick(() => {
this.person.age++; // 父子组件都会更新
})
}
}
6. @Provide / @Consume ------ 全局共享状态(依赖注入)
像一个大仓库,所有组件都能拿共享数据。
适用于"登录状态""主题"等全局信息。
示例
上层组件:
typescript
@Entry
@Component
struct Root {
@Provide theme: string = "dark";
build() {
Column() {
GrandChild()
}
}
}
孙子组件随处可取:
typescript
@Component
struct GrandChild {
@Consume theme: string;
build() {
Text(`当前主题: ${this.theme}`)
}
}
7. @Watch ------ 监听变量变化回调
状态改变时触发一个方法。
示例
typescript
@Component
struct CounterComp {
@State @Watch("onCountChange") count: number = 0;
onCountChange() {
console.log("count变啦!", this.count);
}
build() {
Button(`${this.count}`)
.onClick(() => this.count++)
}
}
V1 小结
如果用一句话总结:
V1 的状态管理功能完整但相对分散,在大型项目中容易出现管理混乱。
为什么有了 V1 还要开发 V2?
V1 的痛点
-
装饰器过多,概念分散
- @State、@Prop、@Link、@Provide、@Consume、@Observed、@ObjectLink...新手容易混淆
-
对象观测不够智能
- @Observed + @ObjectLink 的配合使用比较繁琐
- 嵌套对象的深度监听支持不够好
-
性能优化困难
- 没有明确的组件复用机制
- 缺少计算属性等现代框架常见特性
-
类型安全不足
- 在 TypeScript 环境下类型推导不够友好
-
与现代前端框架理念脱节
- 缺少类似 Vue 的 computed、React 的 useMemo 等特性
- 事件回调传递不够优雅
V2 的优势
- 统一的状态管理模型 - 基于 @ObservedV2 的统一可观测对象
- 更好的类型支持 - 完善的 TypeScript 类型推导
- 性能优化内置 - @ReusableV2、@Computed 等性能优化装饰器
- 现代化 API 设计 - @Event 事件回调、@Monitor 监听等更符合现代开发习惯
- 更易维护 - 代码结构更清晰,大型项目更好管理
简单说:V2 是吸取了 V1 的经验教训,参考了 Vue、React 等现代框架的优秀设计,打造的新一代状态管理体系。
三、状态管理 V2(新体系,全新的统一语法)
HarmonyOS Next 开始推出 V2 状态管理体系,目标是:
- 更统一
- 更清晰
- 写法更简洁
- 更容易维护大型项目
V2 的主要装饰器有:
@ComponentV2@ObservedV2@Local@Param@Once@Event@Provider / @Consumer@Monitor@Computed@Type@Require
1. @ComponentV2 ------ 定义组件的新方式
V2 必须用这个来声明组件。
typescript
@ComponentV2
struct MyComp {
build() {
Text("Hello V2")
}
}
可以理解为新版的 @Component。
2. @ObservedV2 ------ 可观察数据源(V2 核心)
类装饰器,用于定义可观察的数据模型类。
类似传统框架里的 Model,对象内属性改变 UI 自动更新。
示例
typescript
@ObservedV2
class CounterModel {
count: number = 0;
name: string = "计数器";
}
在组件中使用:
typescript
@ComponentV2
struct Counter {
model: CounterModel = new CounterModel();
build() {
Column() {
Text(`${this.model.name}: ${this.model.count}`)
Button("加一")
.onClick(() => this.model.count++)
}
}
}
3. @Local ------ 本地状态(类似 V1 的 @State)
组件自己的状态,只在当前组件存在。
typescript
@ComponentV2
struct MyComp {
@Local value: number = 0;
build() {
Text(`${this.value}`)
}
}
4. @Param ------ 父组件传来的参数(类似 @Prop)
typescript
@ComponentV2
struct ChildComp {
@Param title: string = "";
build() {
Text(this.title)
}
}
5. @Once ------ 只初始化一次的参数
用于父组件传递但不希望后续更新的参数。
typescript
@ComponentV2
struct ChildComp {
@Once id: string = "";
build() {
Text(`ID: ${this.id}`)
}
}
6. @Event ------ 子向父回调事件
类似 Web 中的事件回调 function prop,这是 V2 的一大亮点!
示例
父组件:
typescript
@ComponentV2
struct Parent {
@Local count: number = 0;
build() {
Child({ onAdd: () => this.count++ })
}
}
子组件:
typescript
@ComponentV2
struct Child {
@Event onAdd: () => void = () => {};
build() {
Button("加一")
.onClick(() => this.onAdd())
}
}
非常优雅!
7. @Provider / @Consumer ------ 依赖注入(V2 延续)
注意 V2 中是 @Consumer 不是 @Consume
typescript
@ComponentV2
struct Root {
@Provider theme: string = "dark";
build() {
GrandChild()
}
}
@ComponentV2
struct GrandChild {
@Consumer theme: string = "";
build() {
Text(`主题: ${this.theme}`)
}
}
8. @Monitor ------ 监控字段变化(类似 V1 的 @Watch)
typescript
@ComponentV2
struct Counter {
@Local count: number = 0;
@Monitor("count")
onCountChange() {
console.log("count changed!", this.count);
}
build() {
Button(`${this.count}`)
.onClick(() => this.count++)
}
}
9. @Computed ------ 派生属性(自动计算)
跟 Vue 的 computed 一样:
示例
typescript
@ComponentV2
struct Calculator {
@Local num: number = 10;
@Computed
get double(): number {
return this.num * 2;
}
build() {
Column() {
Text(`数字: ${this.num}`)
Text(`双倍: ${this.double}`)
Button("加一")
.onClick(() => this.num++)
}
}
}
当 num 改变时,double 自动更新!
10. @Type ------ 标注复杂类型
主要用于数组、集合等复杂类型的观测。
typescript
@ObservedV2
class DataModel {
@Type(Person)
users: Person[] = [];
}
11. @Require ------ 必传参数
用于标记组件的必传属性。
typescript
@ComponentV2
struct UserCard {
@Require @Param name: string = "";
build() {
Text(this.name)
}
}
四、V1 和 V2 的对照表(超清晰)
| 作用 | V1 | V2 |
|---|---|---|
| 本地状态 | @State | @Local |
| 父传子(可变) | @Prop | @Param |
| 父传子(不变) | - | @Once |
| 双向绑定 | @Link | -(推荐使用 @ObservedV2 模型) |
| 可观察对象 | @Observed/@ObjectLink | @ObservedV2 |
| 事件回调 | 普通函数传递 | @Event |
| 监听字段 | @Watch | @Monitor |
| 派生属性 | - | @Computed |
| 全局共享 | @Provide/@Consume | @Provider/@Consumer |
| 组件定义 | @Component | @ComponentV2 |
| 必传参数 | - | @Require |
| 复杂类型 | - | @Type |
五、写个完整 Demo(V2)
让你快速感受新体系的优雅。
typescript
@ObservedV2
class TodoItem {
title: string;
done: boolean;
constructor(title: string) {
this.title = title;
this.done = false;
}
}
@ObservedV2
class TodoStore {
@Type(TodoItem)
todos: TodoItem[] = [];
addTodo(title: string) {
this.todos.push(new TodoItem(title));
}
}
@ComponentV2
struct TodoApp {
store: TodoStore = new TodoStore();
@Local input: string = "";
build() {
Column() {
Row() {
TextInput({ placeholder: "输入待办事项" })
.onChange((value) => this.input = value)
Button("添加")
.onClick(() => {
this.store.addTodo(this.input);
this.input = "";
})
}
ForEach(this.store.todos, (item: TodoItem) => {
Row() {
Checkbox()
.select(item.done)
.onChange((checked) => item.done = checked)
Text(item.title)
}
})
}
}
}
是不是清爽很多?
六、总结:如何形容 V1 和 V2?
如果让我用一句最形象的话总结:
- V1 像一个装满了各种工具的工具箱,能用但比较分散,需要你记住每个工具的用法。
- V2 像新装修后的智能家居系统,统一、自动、高效、好维护,更符合现代开发理念。
建议
- 新项目: 优先使用 V2 状态管理体系
- 老项目: 可以逐步迁移,V1 和 V2 可以在同一项目共存
- 学习路径: 了解 V1 基础 → 重点掌握 V2 → 实战中深化理解
HarmonyOS 的状态管理正在变得越来越成熟和易用,希望这篇文章能帮你彻底理解这套体系!
🔥 如果觉得有帮助,别忘了点赞收藏哦!有问题欢迎评论区交流!