在鸿蒙(HarmonyOS)应用开发中,组件间通信是构建复杂界面的核心能力。ArkUI框架提供了多种通信机制,根据数据流向、组件层级和场景需求,可分为以下六类方式:
📥 1. 单向数据传递(父→子)
-
装饰器 :
@Prop
-
场景:父组件向子组件传递只读数据,子组件接收后无法反向修改父组件状态。
-
特点:
- 父组件使用
@State
管理数据,子组件通过@Prop
接收。 - 父组件数据更新时,子组件自动同步刷新,但子组件对
@Prop
的修改仅限本地,不影响父源。
- 父组件使用
-
示例:
scss// 父组件 @Component struct Parent { @State message: string = "Hello"; build() { Column() { Child({ message: this.message }) } } } // 子组件 @Component struct Child { @Prop message: string; // 仅接收数据 build() { Text(this.message) } }
🔄 2. 双向数据绑定(父↔子)
-
装饰器 :
@Link
-
场景:父子组件需共享同一数据源,任何一方修改都需实时同步(如表单输入)。
-
特点:
- 父组件用
@State
定义数据,通过$变量名
(如$count
)传递给子组件的@Link
变量。 - 子组件修改数据后,父组件状态自动更新。
- 父组件用
-
示例:
less// 父组件 @Component struct Parent { @State count: number = 0; build() { Column() { Child({ count: $count }) } } } // 子组件 @Component struct Child { @Link count: number; // 双向绑定 build() { Button('+1').onClick(() => this.count++) } }
🌐 3. 跨层级通信(祖先→后代)
-
装饰器 :
@Provide
和@Consume
-
场景:跨多级组件共享数据(如主题配置、用户信息),避免逐层传递。
-
特点:
- 祖先组件用
@Provide
暴露数据,后代组件通过@Consume
直接消费。 - 支持双向同步:后代修改数据会触发祖先状态更新。
- 祖先组件用
-
示例:
scss// 祖先组件 @Component struct GrandParent { @Provide theme: string = "dark"; build() { Parent() } } // 后代组件(无需经过中间组件) @Component struct Child { @Consume theme: string; build() { Text(`当前主题:${this.theme}`) } }
📡 4. 事件机制(任意组件间)
-
工具 :
Emitter
或EventHub
-
场景:非父子关系的组件通信,或需要解耦的跨页面事件通知。
-
特点:
- Emitter :轻量级事件管理,支持复杂数据传递,通过
eventId
标识事件。 - EventHub:类似事件总线,通过字符串事件名通信,更接近Vue.js模式。
- Emitter :轻量级事件管理,支持复杂数据传递,通过
-
示例(Emitter):
csharp// 发送方 emitter.emit({ eventId: 1 }, { data: "消息内容" }); // 接收方 emitter.on({ eventId: 1 }, (event) => { console.log(event.data); // 输出"消息内容" });
⚡ 5. 回调函数与状态监听
-
方案:
- 事件回调 :子组件通过
props
接收父组件的函数,触发时回调数据(子→父)。 - **
@Watch
装饰器**:监听数据变化并执行自定义逻辑(如日志、校验)。
- 事件回调 :子组件通过
-
示例(事件回调):
less// 父组件定义回调 @Component struct Parent { handleChildEvent(data: string) { ... } build() { Column() { Child({ onEvent: this.handleChildEvent }) } } } // 子组件触发回调 @Component struct Child { onEvent: (data: string) => void; // 声明回调类型 build() { Button('提交').onClick(() => this.onEvent('数据')) } }
🧩 6. 对象同步(复杂数据类型)
-
装饰器 :
@ObjectLink
+@Observed
-
场景:同步嵌套对象或数组的修改(如表单编辑对象属性)。
-
特点:
- 需用
@Observed
标记类,@ObjectLink
绑定对象实例。 - 修改对象的属性会触发UI更新。
- 需用
-
示例:
less@Observed class User { name: string = ''; } // 子组件 @Component struct ProfileEditor { @ObjectLink user: User; // 绑定对象 build() { TextInput({ text: this.user.name }) } }
📊 各通信方式对比与选型建议
方式 | 数据流向 | 适用场景 | 复杂度 |
---|---|---|---|
@Prop |
父→子(单向) | 展示型组件接收数据 | ⭐☆☆ |
@Link |
父↔子(双向) | 表单控件等需双向同步的场景 | ⭐⭐☆ |
@Provide /@Consume |
跨层级双向 | 全局配置、多级组件共享状态 | ⭐⭐☆ |
Emitter /EventHub |
任意方向 | 解耦通信、跨页面事件 | ⭐⭐☆ |
事件回调 | 子→父 | 子组件触发父逻辑(如提交结果) | ⭐⭐☆ |
@ObjectLink |
对象属性同步 | 编辑复杂对象的嵌套属性 | ⭐⭐⭐ |
选型原则:
- 优先使用装饰器方案 (如
@Prop
、@Link
)实现直接父子通信,保证类型安全; - 跨层级或全局状态用
@Provide
/@Consume
; - 非父子组件解耦通信选事件总线 (
EventHub
)或Emitter; - 避免过度设计:简单场景勿用复杂方案(如
@ObjectLink
仅限嵌套对象同步需求)。
如需进一步实践案例,可参考:鸿蒙状态管理最佳实践。