HarmonyOS7 状态管理全家桶:@State、@Prop、@Link 到底怎么选?
文章目录
前言
刚学 ArkUI 的时候,状态管理那几个装饰器把我搞懵了——@State、@Prop、@Link,长得像亲兄弟,用起来完全不一样。照着官方文档抄了一遍代码,数据就是不同步,debug 了两个小时才发现用错了装饰器。

选错装饰器,轻则数据不同步,重则整个组件不刷新。
今天把这三个装饰器掰碎了讲,看完你就知道每个场景该用谁。
为什么状态管理这么头疼
ArkUI 是声明式框架,UI 是状态的函数:状态变了,UI 自动更新。但问题来了——
- 父组件的数据,子组件能不能改?
- 改了之后父组件知不知道?
- 孙组件要访问祖先的数据怎么办?
这些问题的答案,就藏在装饰器的选择里。选对了,数据流通顺;选错了,刷新丢失、数据不同步,满屏 bug。
三种装饰器对比
先把最核心的区别摆出来:
| 维度 | @State | @Prop | @Link |
|---|---|---|---|
| 数据流向 | 组件内部,自给自足 | 父 → 子(单向) | 父 ↔ 子(双向) |
| 能否修改 | 随便改 | 能改,但不通知父组件 | 能改,同步通知父组件 |
| 类型限制 | 支持所有类型 | 只支持简单类型(string/number/boolean) | 只支持简单类型 |
| 父组件传参 | 不需要 | 子组件({ prop: this.xxx }) |
子组件({ link: $xxx }) |

| 性能影响 | 低 | 低 | 稍高(双向绑定有监听开销) |
| 使用场景 | 组件私有状态 | 展示型子组件 | 表单、开关等需回传的子组件 |
划重点:@Prop 改了不通知爹,@Link 改了会通知爹。 就这一句话的区别。
场景一:父子单向传递(@Prop)
父组件有个用户名,子组件只负责展示,不需要回传。
@Component
struct ParentA {
@State username: string = '鸿蒙开发者'
build() {
Column() {
Text('父组件:' + this.username)
.fontSize(20)
ChildA({ name: this.username })
}
}
}
@Component
struct ChildA {
@Prop name: string
build() {
Text('子组件展示:' + this.name)
.fontSize(16)
.fontColor('#666')
}
}

关键代码讲解:
@Prop name: string—— 子组件用 @Prop 接收,类型必须和父组件传的一致ChildA({ name: this.username })—— 父组件通过构造参数传递,注意不是$语法- 父组件修改
username,子组件会跟着更新;但子组件改name,父组件的username不变
这是"只读展示"场景的标准用法。 子组件像是拿了一份复印件,随便画,原件不受影响。
场景二:父子双向同步(@Link)
子组件是个开关,用户拨动开关后,父组件的状态也要同步更新。
@Component
struct ParentB {
@State isDarkMode: boolean = false
build() {
Column() {
Text('当前模式:' + (this.isDarkMode ? '深色' : '浅色'))
.fontSize(20)
ToggleChild({ mode: $isDarkMode })
}
}
}
@Component
struct ToggleChild {
@Link mode: boolean
build() {
Toggle({ type: ToggleType.Switch, isOn: this.mode })
.onChange((value: boolean) => {
this.mode = value
})
}
}
关键代码讲解:
@Link mode: boolean—— 子组件用 @Link 声明,建立双向绑定ToggleChild({ mode: $isDarkMode })—— 注意$符号,这是 @Link 传参的固定语法this.mode = value—— 子组件修改 @Link 变量,父组件的isDarkMode同步更新
$语法就是告诉框架:这不是传值,是传引用,两边绑定了。
场景三:跨层级传递(@Provide + @Consume)
爷组件要给孙组件传数据,中间隔了一层,用 @Prop/@Link 得一层层透传,太恶心了。HarmonyOS7 提供了 @Provide 和 @Consume 解决这个问题。
@Component
struct Grandparent {
@Provide theme: string = 'blue'
build() {
Column() {
Text('爷爷的主题色:' + this.theme)
MiddleLayer()
}
}
}
@Component
struct MiddleLayer {
build() {
Column() {
Text('中间层,我不关心主题')
Grandchild()
}
}
}
@Component
struct Grandchild {
@Consume theme: string
build() {
Text('孙子收到的主题色:' + this.theme)
.fontColor(this.theme === 'blue' ? '#1890ff' : '#52c41a')
}
}
关键代码讲解:
@Provide theme—— 祖先组件提供数据,自动向下广播@Consume theme—— 后代组件消费数据,按变量名匹配,不需要中间层转发- 中间层
MiddleLayer完全不需要知道 theme 的存在
这招在大型项目中特别好用。 全局主题、用户信息这类"到处都要用"的数据,用 @Provide/@Consume 最省心。
场景四:列表中的状态管理
列表里每个 item 都有独立状态(比如选中态),这是最容易出 bug 的地方。
@Component
struct ListDemo {
@State items: SelectItem[] = [
{ id: 1, name: 'ArkTS', selected: false },
{ id: 2, name: 'ArkUI', selected: false },
{ id: 3, name: 'Flex', selected: true }
]
build() {
List() {
ForEach(this.items, (item: SelectItem) => {
ListItem() {
ItemRow({
name: item.name,
selected: item.selected,
onToggle: () => {
item.selected = !item.selected
}
})
}
}, (item: SelectItem) => item.id.toString())
}
}
}
@Component
struct ItemRow {
@Prop name: string
@Prop selected: boolean
onToggle: () => void = () => {}
build() {
Row() {
Text(this.name)
Checkbox().select(this.selected).onChange(() => this.onToggle())
}
}
}
关键代码讲解:
- 列表项状态通过
@Prop传入,单项选中状态用 @Prop 就够了 - 回调函数
onToggle在父组件中修改@State items,触发列表刷新 - ForEach 的第三个参数
(item) => item.id.toString()是键值生成器,别漏了,否则列表更新会出问题
选型决策树
一张图帮你快速决策:
需要状态管理?
│
├─ 组件自己用,不需要传给子组件?
│ └─ ✅ @State
│
├─ 传给子组件,子组件只展示不改?
│ └─ ✅ @Prop
│
├─ 传给子组件,子组件改了要同步回来?
│ └─ ✅ @Link
│
├─ 跨多层组件传递?
│ └─ ✅ @Provide + @Consume
│
└─ 列表中的单项状态?
└─ ✅ @State(父) + @Prop(子) + 回调
写在最后
状态管理就一句话:数据谁拥有,修改权就归谁。 子组件要改父组件的数据,要么用 @Link 双向绑定,要么用回调函数通知父组件自己改。别偷懒直接改 @Prop,改了也白改。
记住这个原则,状态管理的 90% 的坑都能避开。
更多推荐

所有评论(0)