鸿蒙应用开发之@State 装饰器详解:从基本类型到 @Observed/@ObjectLink/@Track 嵌套监听
文章目录
一、引言
在鸿蒙(HarmonyOS)应用开发中,ArkTS 框架提供了一套强大的响应式编程模型。其中,@State 装饰器是实现组件内部状态管理、驱动 UI 自动刷新的核心工具。本文将深入浅出地讲解 @State 的使用方法、监听机制以及最佳实践,帮助你快速掌握这一关键特性。
二、什么是 @State
@State 是 HarmonyOS ArkTS 框架中用于管理组件内部状态的核心装饰器,它实现了数据驱动 UI 的响应式编程。被 @State 修饰的变量变化时,依赖该变量的 UI 组件会自动重新渲染,保持数据与界面的同步。
其底层基于依赖收集和变更通知机制:组件渲染时,框架会收集 @State 变量的 UI 依赖关系;当变量被修改时,框架会通知所有依赖方执行最小化更新,从而高效地刷新界面。
三、监听基本数据类型
对于 boolean、string、number 等基础类型,直接赋值即可被框架观察到并触发 UI 刷新。
@Entry
@Component
struct BasicTypeDemo {
@State count: number = 0;
@State name: string = 'Hello';
build() {
Column({space: 20}) {
Text(`Count: ${this.count}`).fontSize(30)
Text(`Name: ${this.name}`).fontSize(30)
Button('Increment')
.onClick(() => {
this.count++; // 可观察,触发UI刷新
})
Button('Change Name')
.onClick(() => {
this.name = 'ArkTS'; // 可观察,触发UI刷新
})
}
}
}
运行效果如下:

四、监听对象类型
@State 可以观察到对象自身的赋值以及属性赋值的变化,但需要注意:必须通过赋值操作修改对象或其属性,直接修改嵌套子属性(第二层及以下)@State 无法监测。
interface IUser {
name: string
age: number
}
@Entry
@Component
struct ObjectDemo {
@State user:IUser = { name: 'Alice', age: 25 };
build() {
Column({space: 20}) {
Text(`Name: ${this.user.name}`).fontSize(20)
Text(`Age: ${this.user.age}`).fontSize(20)
Button('Update Age')
.onClick(() => {
// ✅ 属性赋值,可观察
this.user.age++
this.user.name = "Beer"
})
Button('Replace Object')
.onClick(() => {
// ✅ 整体赋值,可观察
this.user = { name: 'Bob', age: 30 };
})
}
}
}
五、监听嵌套对象
示例:嵌套对象结构
class Pet {
name: string = '阿旺';
age:number = 2;
}
class Person {
name: string = 'Jerry';
age: number = 18;
pet: Pet = new Pet();
}
@Entry
@Component
struct NestedDemo {
@State person: Person = new Person();
build() {
Column({space: 20}) {
Text(`name: ${this.person.name}`).fontSize(20)
Text(`age: ${this.person.age}`).fontSize(20)
Text(`pet name: ${this.person.pet.name}`).fontSize(20)
Text(`pet age: ${this.person.pet.age}`).fontSize(20)
Button('改变人的年龄')
.onClick(() => {
// ✅ 第一层变化,@State可观察
this.person.age++
})
Button('改变宠物的年龄')
.onClick(() => {
// ❌ 不可观察
this.person.pet.age++
})
}
}
}
上述代码中,当点击第二个按钮 改变宠物的年龄 时,对 this.person.pet.age 重新进行了赋值,但是页面并没有发生变化。
这是因为@State 只支持一层观察,无法递归监听嵌套对象深层属性的变化。对于嵌套对象(二层及以下属性的变化),必须配合 @Observed 和 @ObjectLink 装饰器。
5.1 @Observed
@Observed 是一个类装饰器,用于标记一个类为可观察的。当类被 @Observed 装饰后,其实例的属性变化可以被框架追踪到,即使这些属性是嵌套的深层属性。通常与 @ObjectLink 配合使用,解决 @State 无法监听嵌套对象深层变化的限制。
5.2 @ObjectLink
@ObjectLink 是一个变量装饰器,用于在子组件中接收父组件传递过来的 @Observed 装饰类的实例。它会在子组件中建立对父组件数据的引用,当被引用的属性发生变化时,子组件会自动刷新。
当数据结构包含多层嵌套(如对象中包含对象、数组等),且需要监听深层属性的变化时,使用 @Observed + @ObjectLink 组合是最佳实践。父组件用 @State 持有顶层对象,子组件用 @ObjectLink 接收嵌套对象,从而实现深度响应式监听。
下面使用 @Observed + @ObjectLink,对上面的嵌套对象结构进行改造:
// 使用@Observed装饰被嵌套的类
@Observed
class Pet {
name: string = '阿旺';
age:number = 2;
}
class Person {
name: string = 'Jerry';
age: number = 18;
pet: Pet = new Pet();
}
@Entry
@Component
struct NestedDemo {
@State person: Person = new Person();
build() {
Column({space: 20}) {
Text(`person name: ${this.person.name}`).fontSize(20)
Text(`person age: ${this.person.age}`).fontSize(20)
PetComponent({ pet: this.person.pet })
Button('改变人的年龄')
.onClick(() => {
this.person.age++
})
Button('改变宠物的年龄')
.onClick(() => {
// 现在可以观察到嵌套属性的变化
this.person.pet.age++
})
}
}
}
@Component
struct PetComponent {
@ObjectLink pet: Pet;
build() {
Column({space: 20}) {
Text(`component pet name: ${this.pet.name}`).fontSize(20)
Text(`component pet age: ${this.pet.age}`).fontSize(20)
}
}
}
5.4 @Track
上述代码虽然实现了对深层次数据变化的监听,但依然存在问题。为了验证这个问题,我们对代码进行如下改造:
// ...
@Entry
@Component
struct NestedDemo {
//...
isRender(tag: string): number {
console.log(`Text ${tag} is rendered`);
return 20;
}
build() {
Column({space: 20}) {
Text(`person name: ${this.person.name}`).fontSize(this.isRender("person name"))
Text(`person age: ${this.person.age}`).fontSize(this.isRender("person age"))
PetComponent({ pet: this.person.pet })
Button('改变人的年龄')
.onClick(() => {
this.person.age++
})
Button('改变宠物的年龄')
.onClick(() => {
this.person.pet.age++
})
}
}
}
//...
添加了 isRender(tag: string) 方法,打开 Log 窗口,点击 改变人的年龄 按钮,可以看到:

问题分析:咱们只修改了人的年龄,但人名那个文本也被重新渲染了,这明显不合适,造成了过度渲染。这种过度渲染会带来以下问题:
- 性能浪费:不必要的UI更新消耗CPU和GPU资源
- 响应延迟:大量不必要的渲染操作会降低应用响应速度
- 电池消耗:频繁的UI更新会增加设备功耗
要想解决这个问题,可以使用 @Track 装饰器。
@Track 装饰器是鸿蒙ArkTS框架中用于精细化状态管理的工具,它能够精确追踪对象内部特定属性的变化,仅在标记的属性发生变更时触发UI更新。这解决了传统状态管理(如@State)中“过度渲染”的问题——传统方式下,即使对象只有一个属性变化,也会导致整个组件重新渲染,造成性能浪费。
在类定义中,使用 @Track 装饰需要被观察的属性:
//...
class Person {
+ @Track name: string = 'Jerry';
+ @Track age: number = 18;
+ @Track pet: Pet = new Pet();
}
@Entry
@Component
struct NestedDemo {//...}
//...
上面咱们对 name、 age、 pet 三个属性都使用 @Track 进行了修饰,可能你会疑惑咱们只是修改的 age 属性的值,其他两个为啥也要用 @Track 修饰呢?
这是是因为,未使用 @Track 标记的属性不能在 UI 组件中使用,否则会报错。name、pet 属性,咱们都用在UI组件中使用了,为了防止报错,所以都用 @Track 进行了修饰。
注意:可以在非 UI 组件中使用非 @Track 装饰的属性,如事件回调函数中、生命周期函数中等。
5.5 与 @Observed 的区别
@Observed是类装饰器,作用于整个类,使类的所有属性都可被观察。@Track是属性装饰器,作用于类中的具体属性,只标记需要被观察的字段。- 当只需要监听嵌套对象中的某几个属性时,使用
@Track更加轻量和精确,避免不必要的性能开销。
六、总结对比
| 数据类型 | 监听方式 | 可观察的修改 | 注意事项 |
|---|---|---|---|
| 基本类型(number/string/boolean) | @State |
直接赋值 | 最基础的响应式用法,性能最佳 |
| 对象(第一层属性) | @State |
整体赋值、属性赋值 | 嵌套属性(第二层及以下)不可观察 |
| 嵌套对象(第二层及以下) | @Observed + @ObjectLink |
深层属性变化可观察 | 类需用 @Observed 装饰,子组件用 @ObjectLink 接收 |
| 嵌套对象(精确控制) | @Track |
仅标记的属性变化可观察 | 避免过度渲染,提升性能;未标记的属性不能在 UI 中使用 |
七、关键要点
@State变量必须初始化,且通过this.xxx = value修改,支持基本类型和对象及对象第一层属性的变化监听。- 嵌套对象的深层变化需要
@Observed/@ObjectLink配合实现深度观察,父组件用@State持有顶层对象,子组件用@ObjectLink接收嵌套对象。 @Track装饰器用于精确控制属性观察,仅标记的属性变化才触发 UI 更新,可有效避免过度渲染问题;但未标记@Track的属性不能在 UI 中使用,否则会导致 JSCrash。
更多推荐


所有评论(0)