彻底搞懂 ArkTS 状态管理 V1:8 个核心装饰器全解析
ArkTS 声明式 UI 的核心思想是 “数据驱动 UI”:UI 是状态的映射,状态变化时,UI 会自动重新渲染。鸿蒙官方提供的状态管理 V1是一套基于装饰器的轻量级状态管理方案数据所有权:谁拥有数据,谁负责初始化和销毁数据传递:数据如何在组件之间传递数据响应:数据变化后如何触发 UI 刷新和自定义逻辑状态管理 V1 是鸿蒙开发中 90% 场景下的首选方案,它简单、高效、开箱即用,不需要引入任何第
彻底搞懂 ArkTS 状态管理 V1:8 个核心装饰器全解析
在 HarmonyOS/OpenHarmony 的声明式 UI 开发中,状态管理是最核心、最基础也是最容易踩坑的部分。很多开发者刚接触 ArkTS 时,都会被 @State、@Prop、@Link 等一堆装饰器搞得晕头转向。
本文完全基于鸿蒙官方最新的状态管理 V1 文档,用最清晰的结构、最准确的定义和最实用的示例,带你一次性搞懂所有 8 个核心状态装饰器。
一、什么是状态管理 V1?
ArkTS 声明式 UI 的核心思想是 “数据驱动 UI”:UI 是状态的映射,状态变化时,UI 会自动重新渲染。
鸿蒙官方提供的状态管理 V1是一套基于装饰器的轻量级状态管理方案,专门解决以下三个核心问题:
- 数据所有权:谁拥有数据,谁负责初始化和销毁
- 数据传递:数据如何在组件之间传递
- 数据响应:数据变化后如何触发 UI 刷新和自定义逻辑
状态管理 V1 是鸿蒙开发中 90% 场景下的首选方案,它简单、高效、开箱即用,不需要引入任何第三方库。
二、基础状态管理:组件内与父子组件
这是你每天都会用到的最基础的状态管理能力,解决 “组件自己用” 和 “父子组件之间传值” 的问题。
2.1 @State:组件内状态
官方定义:@State 装饰的变量拥有其所属组件的状态,可以作为其子组件单向和双向同步的数据源。当其数值改变时,会引起相关组件的渲染刷新。
核心特性:
- ✅ 拥有数据:数据是组件私有的,组件销毁时数据一起销毁
- ✅ 必须初始化:声明时必须赋予初始值
- ✅ 双向绑定:变量变化自动刷新 UI,UI 输入也能自动更新变量
- ✅ 观察范围:基本类型、对象引用的变化
使用示例:
@Entry
@Component
struct Counter {
// 组件私有状态,必须初始化
@State count: number = 0
build() {
Column() {
Text(`当前计数:${this.count}`)
Button("+1")
.onClick(() => this.count++)
}
}
}
2.2 @Prop:父子单向同步
官方定义:@Prop 装饰的变量可以和父组件建立单向同步关系,@Prop 装饰的变量是可变的,但修改不会同步回父组件。
核心特性:
- ❌ 不拥有数据:数据来自父组件
- ❌ 不能初始化:必须由父组件传入
- ➡️ 单向同步:父组件变化自动同步到子组件,子组件变化不会同步回父组件
- ✅ 子组件可修改:子组件可以修改,但修改只在子组件内部生效
使用示例:
// 父组件
@Entry
@Component
struct Parent {
@State message: string = "Hello from Parent"
build() {
Column() {
Text(`父组件:${this.message}`)
// 父组件将数据传给子组件
Child({ message: this.message })
}
}
}
// 子组件
@Component
struct Child {
// 子组件接收数据,不能初始化
@Prop message: string
build() {
Column() {
Text(`子组件:${this.message}`)
Button("修改消息")
.onClick(() => {
// 子组件可以修改,但不会影响父组件
this.message = "Hello from Child"
})
}
}
}
2.3 @Link:父子双向同步
官方定义:@Link 装饰的变量可以和父组件建立双向同步关系,子组件中 @Link 装饰变量的修改会同步给父组件中建立双向数据绑定的数据源,父组件的更新也会同步给 @Link 装饰的变量。
核心特性:
- ❌ 不拥有数据:数据来自父组件
- ❌ 不能初始化:必须由父组件传入
- ↔️ 双向同步:父子组件任何一方修改,另一方都会自动同步
- ✅ 子组件可修改:子组件修改会直接影响父组件
使用示例:
// 父组件
@Entry
@Component
struct Parent {
@State count: number = 0
build() {
Column() {
Text(`父组件:${this.count}`)
// 父组件使用$语法传递双向绑定
Child({ count: $count })
}
}
}
// 子组件
@Component
struct Child {
// 子组件接收双向绑定数据
@Link count: number
build() {
Column() {
Text(`子组件:${this.count}`)
Button("+1")
.onClick(() => {
// 子组件修改会同步回父组件
this.count++
})
}
}
}
三、跨层级状态管理:@Provide/@Consume
当组件层级超过 3 层时,用 @Link 一层一层传参就会变得非常繁琐,中间组件被迫成为 “传话筒”。这时候就需要跨层级状态管理。
官方定义:@Provide/@Consume 装饰的变量用于跨组件层级(多层组件)同步状态变量,可以不需要通过参数命名机制传递,通过 alias(别名)或者属性名绑定。
核心特性:
- ✅ @Provide 拥有数据:只有提供数据的组件拥有数据
- ❌ @Consume 不拥有数据:后代组件直接消费数据
- ↔️ 双向同步:任何地方修改,所有使用该数据的地方都会自动刷新
- 🚫 中间组件无需参与:数据直接从祖先组件传递到后代组件
使用示例:
// 爷爷组件:提供数据
@Entry
@Component
struct Grandfather {
// 提供数据,所有后代都能拿到
@Provide("theme") theme: string = "light"
build() {
Column() {
Text(`爷爷:当前主题是${this.theme}`)
Button("切换主题")
.onClick(() => {
this.theme = this.theme === "light" ? "dark" : "light"
})
// 什么都不用传给爸爸
Father()
}
}
}
// 爸爸组件:完全不需要管这个数据
@Component
struct Father {
build() {
Column() {
Text("我是爸爸,我什么都没传")
// 什么都不用传给儿子
Son()
}
}
}
// 儿子组件:直接消费数据
@Component
struct Son {
// 通过别名直接消费数据
@Consume("theme") theme: string
build() {
Text(`儿子:当前主题是${this.theme}`)
}
}
四、复杂对象状态管理:@Observed/@ObjectLink
这是鸿蒙状态管理中最容易踩坑的部分。基础状态装饰器只能观察对象引用的变化,不能观察对象内部属性的变化,更不能观察嵌套对象的属性变化。
4.1 @Observed:标记可观察类
官方定义:@Observed 装饰 class,需要观察多层嵌套场景的 class 需要被 @Observed 装饰。单独使用 @Observed 没有任何作用,需要和 @ObjectLink、@Prop 联用。
核心特性:
- ✅ 只能装饰类:不能装饰变量
- ✅ 开启深度观察:告诉 ArkTS 这个类的实例需要被深度观察
- ❌ 单独使用无效:必须和 @ObjectLink 或 @Prop 联用
4.2 @ObjectLink:观察对象内部属性
官方定义:@ObjectLink 装饰的变量接收 @Observed 装饰的 class 的实例,应用于观察多层嵌套场景,和父组件的数据源构建双向同步。
核心特性:
- ❌ 不拥有数据:数据来自父组件
- ❌ 不能初始化:必须由父组件传入
- ↔️ 双向同步:子组件修改对象内部属性,父组件也会同步
- ✅ 观察范围:对象内部任意属性的变化,包括嵌套对象
使用示例:
// 第一步:给所有需要观察的类加上@Observed
@Observed
class User {
name: string
address: Address
constructor(name: string, city: string) {
this.name = name
this.address = new Address(city)
}
}
@Observed
class Address {
city: string
constructor(city: string) {
this.city = city
}
}
// 父组件
@Entry
@Component
struct Parent {
@State user: User = new User("张三", "北京")
build() {
Column() {
Text(`父:${this.user.name},住在${this.user.address.city}`)
Button("修改城市为上海")
.onClick(() => {
// 现在修改嵌套对象属性,UI会刷新了
this.user.address.city = "上海"
})
Child({ user: this.user })
}
}
}
// 子组件
@Component
struct Child {
// 用@ObjectLink代替@Link
@ObjectLink user: User
build() {
Column() {
Text(`子:${this.user.name},住在${this.user.address.city}`)
Button("修改城市为广州")
.onClick(() => {
// 子组件修改,父组件也会同步刷新
this.user.address.city = "广州"
})
}
}
}
五、状态变化监听:@Watch
前面的所有装饰器都只解决 “数据变化→UI 刷新” 的问题,而 @Watch 解决 “数据变化→执行自定义逻辑” 的问题。
官方定义:@Watch 应用于对状态变量的监听。如果开发者需要关注某个状态变量的值是否改变,可以使用 @Watch 为状态变量设置回调函数。
核心特性:
- ❌ 不拥有数据:只是状态变化的观察者
- ✅ 自动执行:当被监听的变量变化时,自动调用回调函数
- ✅ 严格相等判断:使用严格相等(===)来判断值是否变化
- ✅ 执行时机:在 UI 刷新之前执行
使用示例:
@Entry
@Component
struct ShoppingCart {
// 监听单价和数量的变化
@Watch('updateTotal')
@State price: number = 10
@Watch('updateTotal')
@State quantity: number = 1
// 总价是衍生值
total: number = 10
build() {
Column() {
Text(`单价:${this.price} 元`)
Text(`数量:${this.quantity} 件`)
Text(`总价:${this.total} 元`)
Button("单价+1")
.onClick(() => this.price++)
Button("数量+1")
.onClick(() => this.quantity++)
}
}
// 只要单价或数量变化,总价就会自动更新
updateTotal() {
this.total = this.price * this.quantity
console.log(`总价更新为:${this.total}`)
}
}
重要注意事项:
- 只能监听被状态装饰器装饰的变量
- 绝对不要在回调里修改被监听的变量本身,否则会导致无限循环
- 回调函数的参数依次是新值和旧值
六、所有装饰器终极对比表
| 装饰器 | 装饰对象 | 数据所有权 | 数据方向 | 能否修改 | 适用场景 |
|---|---|---|---|---|---|
| @State | 组件属性 | 自己拥有 | 内部 | 可改 | 组件私有状态 |
| @Prop | 组件属性 | 父拥有 | 父→子单向 | 可改但不同步 | 只给子组件展示的数据 |
| @Link | 组件属性 | 父拥有 | 父子双向 | 可改 | 简单父子互相修改数据 |
| @Provide | 组件属性 | 自己拥有 | 全局双向 | 可改 | 多层组件共享同一个状态 |
| @Consume | 组件属性 | 祖先拥有 | 全局双向 | 可改 | 后代组件使用共享状态 |
| @Observed | 类 | - | - | - | 复杂对象、嵌套对象的类 |
| @ObjectLink | 组件属性 | 父拥有 | 双向 | 可改 | 子组件修改嵌套对象属性 |
| @Watch | 组件属性 | - | - | - | 数据变化时执行自定义逻辑 |
七、最佳实践建议
- 优先使用 @State:组件内部的状态优先使用 @State,保持状态的私有性
- 单向优先:如果子组件不需要修改数据,优先使用 @Prop 而不是 @Link
- 避免滥用 @Provide/@Consume:只有当组件层级超过 3 层时才使用,否则会降低代码的可读性
- 复杂对象必须加 @Observed:只要是需要观察内部属性变化的类,都要加上 @Observed 装饰器
- @Watch 用于衍生值和副作用:不要在 @Watch 里修改被监听的变量本身
八、总结
本文基于鸿蒙官方文档,全面解析了 ArkTS 状态管理 V1 的 8 个核心装饰器。这些装饰器覆盖了鸿蒙开发中 90% 以上的状态管理场景,是每个鸿蒙开发者必须掌握的基础知识。
掌握了这些装饰器,你就能写出清晰、高效、可维护的声明式 UI 代码。如果你想深入学习,可以参考鸿蒙官方的状态管理 V1 文档,里面有更详细的说明和更多示例。
更多推荐



所有评论(0)