HarmonyOS Next面试题之装饰器@Prop与@Link的区别和使用
本文对比了ArkUI中@Prop和@Link两种装饰器的区别。@Prop实现单向数据流,子组件可修改但不会同步回父组件,适用于纯展示场景;@Link建立双向绑定,子组件修改会立即同步回父组件,适用于表单交互等场景。文章通过计数器示例演示了两种装饰器的具体使用方式,并指出在ArkUI V2版本中,@Link被@Param+@Event组合替代,以遵循单向数据流的最佳实践。
·
一、核心区别
| 特性 | @Prop | @Link |
|---|---|---|
| 数据流向 | 单向:父 → 子 | 双向:父 ⇄ 子 |
| 子组件能否修改 | 可以修改,但不会同步回父组件 | 修改会立即同步回父组件 |
| 初始值来源 | 父组件传递的值或本地默认值 | 必须由父组件传递 $ 绑定的变量 |
| 传递方式 | 值拷贝(深拷贝,对对象类型会递归复制) | 引用传递(建立双向绑定) |
| 性能 | 父组件变化时,子组件会重新渲染 | 任意一端变化,两端都会重新渲染 |
| 适用场景 | 子组件仅需展示父组件数据,或内部临时修改不影响外部 | 子组件需要修改父组件数据(如表单、开关) |
- @Prop 装饰器的作用:父组件将数据单向传递给子组件,子组件通过 @Prop 接收后,可以将其视为自己的局部状态进行修改,但这些修改不会影响父组件。当父组件的源数据变化时,子组件的 @Prop 会自动更新(重新赋值并触发渲染)。
- @Prop 装饰器的使用规则:
-
- 子组件中 @Prop 变量可以初始化默认值(如果父组件未传,则使用默认值)。
-
- 父组件在子组件标签中直接传递普通变量(无需 $ 符号)。
-
- 对于对象类型,@Prop 会进行深拷贝,因此子组件内部修改对象的属性不会影响父组件对象。
- @Link 装饰器的作用:建立父子组件之间的双向同步,子组件通过 @Link 接收父组件的状态,无论哪一方修改该值,另一方都会自动更新。
- @Link 的使用规则:
-
- 子组件中 @Link 变量不能初始化默认值,必须由父组件传递。
-
- 父组件在子组件标签中必须传递被 $ 符号包裹的变量(如 $parentState),表示建立双向绑定。
-
- @Link 传递的是引用,因此子组件内修改对象的属性会直接反映到父组件。
二、场景使用
- 场景示例:父组件有一个计数器,子组件可以显示并修改它。
- 使用 @Prop(单向):
// 父组件
@Entry
@Component
struct Parent {
@State count: number = 10;
build() {
Column() {
Text(`父组件 count: ${this.count}`)
.fontSize(20)
.margin(10)
Button('父组件 +1')
.onClick(() => {
this.count++;
})
Divider()
ChildProp({ childCount: this.count })
}
.width('100%')
.padding(20)
}
}
// 子组件
@Component
struct ChildProp {
@Prop childCount: number; // 单向接收
build() {
Column() {
Text(`子组件 childCount: ${this.childCount}`)
.fontSize(20)
.margin(10)
Button('子组件 +1(仅本地修改)')
.onClick(() => {
// 修改 @Prop 变量,不会影响父组件的 count
this.childCount++;
})
}
}
}
- 运行效果:
-
- 父组件点击按钮,父组件的 count 和子组件的 childCount 都会增加。
-
- 子组件点击按钮,子组件的 childCount 会增加,但父组件的 count 保持不变。
-
- 父组件再次点击,子组件的值会被父组件覆盖(因为单向同步)。
- 使用 @Link(双向):
// 父组件
@Entry
@Component
struct Parent {
@State count: number = 10;
build() {
Column() {
Text(`父组件 count: ${this.count}`)
.fontSize(20)
.margin(10)
Button('父组件 +1')
.onClick(() => {
this.count++;
})
Divider()
ChildLink({ childCount: $count }) // 注意 $ 符号
}
.width('100%')
.padding(20)
}
}
// 子组件
@Component
struct ChildLink {
@Link childCount: number; // 双向绑定
build() {
Column() {
Text(`子组件 childCount: ${this.childCount}`)
.fontSize(20)
.margin(10)
Button('子组件 +1(同步父组件)')
.onClick(() => {
this.childCount++; // 修改会同步回父组件
})
}
}
}
- 运行效果:父组件或子组件任意一方点击按钮,两边的数字都会同步增加。
三、说明
① 对象类型
- @Prop 对对象进行深拷贝,子组件内修改对象属性不会影响父组件。
- @Link 对对象进行引用传递,子组件内修改对象属性会直接影响父组件对象。
② 嵌套深度
- 对于深层嵌套的组件通信,@Prop 和 @Link 需要逐层传递,代码冗余。此时可考虑使用 @Provide / @Consume 或全局状态管理。
③ V2 版本变化
- 在 ArkUI V2(@ComponentV2)中,@Prop 被 @Param 替代(单向),@Link 被 @Param + @Event 的组合替代(通过回调实现双向),不再直接使用 @Link。
四、总结
- 使用 @Prop:单向数据流,当子组件只需要展示父组件传递的数据,并且内部修改不影响父组件时(如纯展示组件、临时编辑但最终不保存),它传递的是深拷贝。
- 使用 @Link:双向数据流,当子组件需要修改父组件的数据,并希望父组件实时感知变化时(如表单输入、开关切换、滑块控制)。父子组件中任一方的修改都会同步给另一方,它传递的是引用,子组件中通过@Link装饰的变量可以像普通变量一样直接修改。
- 为什么 V2 要取消 @Link,改用 @Param + @Event ?为了数据流向更清晰、更可预测。@Link 虽然方便,但子组件可以直接修改父组件的数据,在复杂应用中可能导致数据流难以追踪,V2 的方案强制子组件通过 @Event 回调来“通知”父组件修改,符合“数据向下流动,事件向上传递”的单向数据流最佳实践,让状态变化更可控。
更多推荐


所有评论(0)