在 ArkUI 的组件化开发中,这三个装饰器构成了数据流转的“铁三角”。理解它们的职责边界,本质上是在理解数据所有权(Ownership)与同步开销。
1. 职责边界对比
| 装饰器 | 数据所有权 | 数据流向 | 典型场景 |
|---|---|---|---|
| @State | 组件私有 | 内部驱动 | 定义页面或组件的“源头”数据。 |
| @Prop | 外部传入 | 单向同步 (父 子) | 子组件需要一份数据的本地副本,且不希望影响父组件。 |
| @Link | 外部共享 | 双向同步 (父 子) | 子组件需要直接修改父组件的状态(如:弹窗的开关)。 |
2. @Link 为什么是“双向同步”?
@Link 的底层实现并不是简单的“数值传递”,而是**“引用(Reference)传递”**。
-
原理: 当你将父组件的
@State变量传递给子组件的@Link时,子组件持有的实际上是该变量在状态管理中心的指针。 -
机制: 1. 当父组件修改数据时,状态中心通知父、子组件同时重绘。
- 当子组件通过
@Link修改数据时,它直接触发了状态中心对应的 Setter 拦截器,从而反向驱动父组件(以及其他链接该数据的组件)同步更新。
- 当子组件通过
-
代价: 双向同步意味着更高的耦合度。如果一个数据被
@Link到了 5 层深的子组件中,其中任何一层的误操作都会导致整个组件链条的意外刷新。
3. @Prop 为什么不建议修改?
虽然 ArkUI 允许在子组件内部修改 @Prop 变量,但官方强烈建议将其视为只读。
A. “覆盖”风险 (The Overwrite Issue)
@Prop 会在子组件内部创建一个数据的深拷贝。
- 冲突: 如果子组件修改了本地的
@Prop,而此时父组件的源数据也发生了变化,父组件的变化会强制覆盖子组件的本地修改。这会导致子组件的 UI 瞬间“跳变”回旧状态,产生极难调试的 Bug。
B. 违背单向数据流原则
声明式 UI 的核心是“数据向下流动,事件向上冒泡”。
- 混乱: 如果
@Prop被频繁修改,数据的真实状态(Source of Truth)就会变得模糊——到底是父组件说了算,还是子组件内部偷偷改了?这会增加架构的维护成本。
C. 性能与内存开销
@Prop 涉及对象的深拷贝(尤其是大对象或数组)。
- 开销: 频繁修改一个深拷贝出来的副本,不仅增加了 CPU 负担,还可能导致内存中存在多份不一致的数据镜像。
4. 最佳实践决策树
- 它是组件自己产生的吗? 是:用 @State。
- 子组件只是为了展示它吗? 是:用 @Prop(且只读)。
- 子组件需要改它,且父组件也要跟着变吗? 是:用 @Link。
- 子组件需要改它,但父组件不需要知道? 是:在子组件内部定义新的 @State,并在
aboutToAppear里用父组件传来的值初始化。
架构师的告诫:
优先使用单向数据流。 如果子组件想修改数据,推荐的做法是:父组件传一个 Callback 给子组件,子组件通过调用回调来请求父组件修改 @State。这种“回调模式”比 @Link 更清晰、更易于追踪数据变化。