【HarmonyOS 5】鸿蒙中 @State 的原理详解
适用版本:HarmonyOS NEXT / API 12+ / ArkTS / Stage模型
阅读对象:HarmonyOS初学者、ArkUI开发者、中高级开发工程师
预计阅读时间:60~90分钟
前言
学习 HarmonyOS ArkUI 时,第一个接触的状态管理装饰器几乎都是:
@State
几乎所有官方 Demo 都是这样开始的:
@Entry
@Component
struct Index {
@State count: number = 0
build() {
Column() {
Text(`${this.count}`)
Button("增加")
.onClick(() => {
this.count++
})
}
}
}
很多开发者会认为:
@State 就是一个变量。
事实上完全不是。
真正发生的是:
修改变量
│
▼
ArkUI状态系统监听
│
▼
标记Dirty
│
▼
Scheduler加入刷新队列
│
▼
重新Build()
│
▼
Diff算法
│
▼
更新UI节点
也就是说:
@State真正的作用不是保存数据,而是通知UI刷新。
本文将从:
- @State设计目的
- 编译器原理
- 响应式机制
- Dirty标记
- Build刷新
- Diff算法
- 生命周期
- Observer机制
- 性能优化
- 与Vue/React区别
- 与Observed关系
- 与ObjectLink关系
- 底层源码思想
彻底讲透 HarmonyOS 中 @State。
第一章 什么是 @State?
官方定义:
@State 是组件内部状态。
它具有三个特点:
- 私有
- 可响应
- 自动刷新UI
例如:
@State message: string = "Hello"
页面:
Text(this.message)
修改:
this.message = "HarmonyOS"
页面立即刷新。
所以:
State
=
数据
+
监听
+
刷新
而不是:
普通变量
第二章 为什么需要 @State?
假设没有状态管理。
例如:
let count = 0
页面:
Text(`${count}`)
按钮:
count++
UI不会刷新。
为什么?
因为:
ArkUI不知道:
count变了
所以不会:
重新Build
因此需要:
State
告诉框架:
我发生变化了。
第三章 普通变量和 @State 区别
普通变量:
count++
仅仅修改内存。
不会通知任何人。
而:
@State count
实际上是:
Setter
↓
通知框架
↓
UI刷新
所以:
State
≈
Observable
第四章 一个完整刷新过程
例如:
Button("Add")
点击:
this.count++
实际上经历:
count++
↓
Setter
↓
StateObject
↓
NotifyChange()
↓
Component Dirty
↓
Scheduler
↓
Build()
↓
Diff
↓
Render
整个过程几十微秒完成。
第五章 编译器如何处理 @State?
开发者写:
@State
count = 0
编译后不会还是这样。
编译器大概生成:
private __count =
new ObservedPropertySimplePU(
0,
this,
"count"
)
真正访问:
get count() {
return this.__count.get()
}
set count(v) {
this.__count.set(v)
}
所以:
开发者写:
count++
真正执行:
get()
+
set()
因此:
框架知道:
发生修改
第六章 Setter 做了什么?
Setter里面主要四件事。
第一步:
保存数据:
value = newValue
第二步:
判断是否变化:
old != new
第三步:
通知观察者:
notify()
第四步:
组件Dirty。
最终:
等待下一帧刷新
第七章 为什么不能直接修改对象属性?
例如:
@State
user = {
name:"Tom"
}
修改:
this.user.name = "Jack"
很多新人发现:
UI没有刷新。
为什么?
因为:
State监听的是:
user引用
不是:
user.name
所以:
Setter根本没调用。
正确方式:
this.user = {
...this.user,
name:"Jack"
}
引用变化:
Setter执行
页面刷新。
第八章 为什么数组 push 有时候刷新?
例如:
@State
list = []
执行:
list.push(1)
HarmonyOS 5 中:
数组进行了增强代理。
因此:
push()
↓
通知State
↓
刷新
而:
普通对象:
obj.name="xx"
不会。
所以:
数组和对象机制不同。
第九章 @State 生命周期
组件创建:
Create Component
↓
Create State
↓
注册Observer
↓
第一次Build
组件销毁:
Destroy
↓
解绑Observer
↓
释放State
因此:
State生命周期:
跟组件一致。
第十章 Build 为什么会重新执行?
很多人误以为:
刷新Text
实际上:
ArkUI重新执行:
build()
例如:
build(){
Text(this.count)
Button()
}
count改变:
重新执行:
build()
不是:
重新执行:
Button点击事件
只有:
build()
重新跑。
第十一章 为什么不会整页刷新?
有人担心:
build重新执行
是不是整个页面都重新创建?
答案:
不是。
因为:
后面还有:
Diff
例如:
旧:
Text 10
Button
Image
新:
Text 11
Button
Image
Diff结果:
仅更新Text
Button:
保持原对象
Image:
保持原对象
所以性能非常高。
第十二章 Dirty 标记机制
State修改:
不会立刻刷新。
而是:
Dirty = true
加入:
Dirty Queue
例如:
连续:
count++
count++
count++
count++
不会刷新四次。
而是:
Dirty
↓
下一帧
↓
统一刷新
类似:
React Batched Update。
第十三章 为什么不能在 build 修改 State?
错误:
build(){
this.count++
Text()
}
因为:
build()
↓
修改State
↓
重新build()
↓
再次修改State
↓
死循环
因此官方禁止:
Build里面改State
第十四章 @State 与 @Prop、@Link、@Observed、@ObjectLink 的关系
| 装饰器 | 作用 | 数据流 |
|---|---|---|
@State |
当前组件私有状态 | 组件内部 |
@Prop |
父组件传值 | 单向 |
@Link |
父子双向绑定 | 双向 |
@Observed |
让对象属性具备响应式 | 对象级 |
@ObjectLink |
监听 @Observed 对象 |
引用对象 |
可以理解为:
@State:管理组件自己的状态。@Prop:父组件向子组件传递只读数据。@Link:父子共享同一份可修改的数据。@Observed:让对象内部字段变化也能被观察。@ObjectLink:子组件引用并监听@Observed对象。
第十五章 常见问题与最佳实践
1. @State 适合什么数据?
适合组件内部临时状态,例如:
- 当前计数
- 输入框内容
- 开关状态
- 当前 Tab
- Loading 状态
2. 不适合什么数据?
不建议用于:
- 全局共享状态
- 跨页面共享数据
- 大型业务对象
- 长生命周期数据
这类数据更适合使用应用级状态管理方案。
3. 更新对象数据的建议
优先使用不可变更新:
this.user = {
...this.user,
name: "Jack"
}
如果需要频繁修改对象内部字段,可考虑使用 @Observed 配合 @ObjectLink。
总结
@State 是 HarmonyOS ArkUI 响应式系统中最基础、最核心的状态管理能力。它的价值并不只是“保存数据”,而是在数据变化时,将变化通知框架,由框架完成脏标记、调度刷新、重新执行 build(),并借助 Diff 算法,仅更新真正发生变化的 UI 节点。
理解 @State 的底层机制后,你会更容易掌握整个 ArkUI 状态管理体系,包括 @Prop、@Link、@Observed、@ObjectLink 以及更高级的应用状态管理模式。
一句话概括:
@State= 响应式状态 + 变化通知 + UI 自动刷新,而不仅仅是一个普通变量。
更多推荐

所有评论(0)