HarmonyOS 鸿蒙Next中父子通信
HarmonyOS 鸿蒙Next中父子通信 不使用prop能做到父传子的通信吗?
【解决方案】
- 方案一:使用应用全局UI状态存储AppStorageV2实现UI组件同步,使用方法可参考在两个页面之间存储数据。
- 方案二:使用EventHub或者Emitter自定义事件发布订阅的方式,让页面A订阅页面B中的数据变化。详细使用请参考关于emitter、eventHub的使用场景。
更多关于HarmonyOS 鸿蒙Next中父子通信的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
楼主,在HarmonyOS开发中,即使不使用 @Prop 装饰器,仍有多种方式可以实现父组件向子组件传递数据。以下是常见的替代方案及实现思路:
一、使用 @Link 装饰器(双向同步)
虽然 @Link 主要用于双向数据同步,但它仍然通过属性传递数据,只是装饰器类型不同。父组件传递状态变量,子组件通过 @Link 接收并可直接修改父组件数据。
// 父组件
@Entry
@Component
struct Parent {
@State parentData: string = "初始数据";
build() {
Column() {
ChildWithLink({ parentData: $parentData }) // 使用$符号传递双向绑定
}
}
}
// 子组件
@Component
struct ChildWithLink {
[@Link](/user/Link) parentData: string; // 接收父组件数据
build() {
Text(this.parentData)
}
}
二、使用 @Provide 和 @Consume(跨层级传递)
通过依赖注入实现跨层级通信,父组件提供数据(@Provide),子组件直接消费(@Consume),无需逐层传递。
// 父组件
@Entry
@Component
struct Parent {
[@Provide](/user/Provide)("sharedData") data: string = "共享数据";
build() {
Column() {
ChildComponent()
}
}
}
// 子组件(可以是任意层级的子孙组件)
@Component
struct ChildComponent {
[@Consume](/user/Consume)("sharedData") childData: string; // 直接获取父组件数据
build() {
Text(this.childData)
}
}
三、通过事件或回调函数
父组件将方法作为参数传递给子组件,子组件通过调用该方法触发父组件逻辑,间接传递数据(适用于动态交互场景)。
// 父组件
@Entry
@Component
struct Parent {
@State receivedData: string = "";
// 定义回调函数
handleChildData = (data: string) => {
this.receivedData = data;
};
build() {
Column() {
ChildComponent({ onDataChange: this.handleChildData })
Text(`子组件传递的数据:${this.receivedData}`)
}
}
}
// 子组件
@Component
struct ChildComponent {
private onDataChange: (data: string) => void; // 接收父组件回调函数
build() {
Button("传递数据给父组件")
.onClick(() => {
this.onDataChange("来自子组件的数据");
})
}
}
四、全局状态管理(AppStorage/LocalStorage)
通过全局状态管理工具(如 AppStorage)实现数据共享,父组件设置数据,子组件直接读取。
// 父组件设置数据
AppStorage.SetOrCreate("globalData", "全局数据");
// 子组件获取数据
@Component
struct ChildComponent {
@StorageLink("globalData") childData: string;
build() {
Text(this.childData)
}
}
五、事件总线(EventBus)
通过自定义事件中心实现组件间通信,父组件发布事件,子组件订阅并接收数据。
// 定义事件总线(简化示例)
class EventBus {
static emit(event: string, data: any) { /* 发布事件逻辑 */ }
static on(event: string, callback: (data: any) => void) { /* 订阅事件逻辑 */ }
}
// 父组件发布数据
EventBus.emit("parentToChild", "通过事件传递的数据");
// 子组件订阅数据
@Component
struct ChildComponent {
@State data: string = "";
onPageShow() {
EventBus.on("parentToChild", (data: string) => {
this.data = data;
});
}
build() {
Text(this.data)
}
}
选择建议
找HarmonyOS工作还需要会Flutter的哦,有需要Flutter教程的可以学学大地老师的教程,很不错,B站免费学的哦:https://www.bilibili.com/video/BV1S4411E7LY/?p=17
在 ArkTS 中,除了 @Prop
,你还可以使用 @Provide
和 @Consume
装饰器来实现,这种方式尤其适合跨层级(例如直接从父组件传给孙子组件,而无需经过中间的子组件)的数据传递。
通俗解释
想象一下家庭聚餐:
- 传统方式 (
@Prop
):就像爸爸(父组件)把一筐水果(数据)给了大儿子(直接子组件),大儿子再从中拿出一些传给小孙子(孙子组件)。大儿子是必须经过的中间人。 - 新方式 (
@Provide
/@Consume
):就像爸爸(父组件)在家里的公共区域(比如客厅茶几)放了一筐水果(@Provide
数据),然后小孙子(任何后代组件)可以直接跑到客厅拿水果吃(@Consume
数据)。不需要大儿子在中间传递。
@Provide
和 @Consume
就像建立了一个家族内部的“共享储物柜”,父组件把东西放进去,任何子组件或孙子组件都可以直接来取用。
具体例子:使用 @Provide
和 @Consume
我们用一个简单的计数器例子来说明。父组件提供一个计数器,孙子组件直接消费并显示它,中间的子组件完全不需要参与数据的传递。
1. 父组件 (爷爷) - 提供数据
父组件使用 @Provide
装饰器来声明一个可供后代组件使用的数据。
// Father.ets
@Entry
@Component
struct Father {
// 关键点:使用 @Provide 装饰器,提供一个名为 "reviewVote" 的数据
@Provide("reviewVote") reviewVotes: number = 0; // 初始值为0
build() {
Column() {
// 显示父组件自己的计数器值
Text(`爸爸的计数器: ${this.reviewVotes}`)
.fontSize(30)
.margin(10)
// 一个按钮,点击后父组件的计数器会增加
Button('爸爸点我+1')
.onClick(() => {
this.reviewVotes++; // 修改 @Provide 变量的值
})
.margin(10)
// 引入子组件。注意:这里没有向子组件传递任何数据!
Son() // ⬅️ 看这里,没有传递参数!
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
2. 子组件 (爸爸) - 中间人
子组件不需要做任何和数据传递相关的事情,它只是负责布局,包含了孙子组件。
// Son.ets
@Component
struct Son {
build() {
Column() {
// 子组件只显示一个文本,不关心数据
Text('我是儿子组件,我不管数据')
.fontSize(20)
.margin(10)
// 引入孙子组件
GrandSon() // ⬅️ 这里也没有传递参数!
}
}
}
3. 孙子组件 - 消费数据
孙子组件使用 @Consume
装饰器来自动找到并接收父组件(爷爷)提供的同名数据。
// GrandSon.ets
@Component
struct GrandSon {
// 关键点:使用 @Consume 装饰器,消费名为 "reviewVote" 的数据
// 系统会自动向上查找,找到爷爷提供的 @Provide("reviewVote")
@Consume("reviewVote") reviewVotes: number // 这里不需要初始化,会自动注入
build() {
Column() {
// 显示从爷爷那里直接得到的计数器值
Text(`孙子的计数器: ${this.reviewVotes}`)
.fontSize(30)
.margin(10)
// 一个按钮,点击后也会改变计数器的值
Button('孙子点我+1')
.onClick(() => {
this.reviewVotes++; // 修改 @Consume 变量的值
})
.margin(10)
}
}
}
运行效果
- 屏幕会显示三个部分:
- “爸爸的计数器: 0”
- “我是儿子组件,我不管数据”
- “孙子的计数器: 0”
- 当你点击爸爸的按钮,爸爸和孙子的计数器会同时变成 1。
- 当你点击孙子的按钮,爸爸和孙子的计数器会同时变成 2。
总结
特性 | @Prop |
@Provide / @Consume |
---|---|---|
数据流向 | 单向:父 → 子 | 双向:父 ↔️ 后代(子、孙等) |
传递路径 | 必须通过直接子组件一层层传递 | 直接跨层级传递,无需中间组件参与 |
适用场景 | 简单的父子通信 | 复杂的多层组件通信,需要跨层级共享数据 |
通过@Link装饰器实现双向同步:父组件通过状态变量初始化子组件的@Link变量,建立双向绑定
// 子组件
@Component
struct ChildComponent {
[@Link](/user/Link) data: number; // 必须由父组件初始化
build() {
Text(`${this.data}`)
}
}
// 父组件
@Component
struct ParentComponent {
@State parentData: number = 100;
build() {
Column() {
ChildComponent({ data: $parentData }) // 使用$符号传递双向绑定
}
}
}
通过回调函数传递数据:父组件将方法通过属性传递给子组件,子组件调用该方法间接获取父组件数据
// 子组件
@Component
struct ChildComponent {
@Prop onReceiveData: (data: string) => void;
build() {
Button('获取父数据').onClick(() => {
this.onReceiveData?.(this.dataFromParent); // 调用父组件传递的方法
})
}
}
// 父组件
@Component
struct ParentComponent {
@State parentMessage: string = "Hello from Parent";
build() {
Column() {
ChildComponent({
onReceiveData: (data) => {
console.log(data); // 通过回调获取子组件操作
}
})
}
}
}
通过事件通信(Emitter/EventHub):子组件监听事件,父组件触发事件时携带数据
// 子组件中注册监听
import emitter from '@ohos.events.emitter';
@Component
struct ChildComponent {
onPageShow() {
emitter.on('parentEvent', (data) => {
console.log('收到父数据:', data);
});
}
}
// 父组件触发事件
@Component
struct ParentComponent {
build() {
Button('发送数据').onClick(() => {
emitter.emit({
eventId: 'parentEvent',
data: { value: 'Data from parent' }
});
})
}
}
通过@Provide和@Inject依赖注入:父组件用@Provide提供数据,子组件通过@Inject注入
// 父组件
@Component
struct ParentComponent {
[@Provide](/user/Provide)('sharedData') data: string = "Parent Data";
build() {
Column() {
ChildComponent()
}
}
}
// 子组件
@Component
struct ChildComponent {
[@Inject](/user/Inject)('sharedData') childData: string;
build() {
Text(this.childData) // 显示"Parent Data"
}
}
-
还可以使用AppStorageAppStorage:应用全局的UI状态存储-管理应用拥有的状态-状态管理(V1)-学习UI范式状态管理-UI开发 (ArkTS声明式开发范式)-ArkUI(方舟UI框架)-应用框架 - 华为HarmonyOS开发者
-
也能结合Observed自己实现一个方法类来共享数据
[@Observed装饰器和@ObjectLink装饰器:嵌套类对象属性变化-管理组件拥有的状态-状态管理(V1)-学习UI范式状态管理-UI开发 (ArkTS声明式开发范式)-ArkUI(方舟UI框架)-应用框架 - 华为HarmonyOS开发者](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-observed-and-objectlink)
2.emitter 事件订阅,同时还可以线程间通信
emitter.emit("calcVoice", { data: { buffer } })
aboutToAppear(): void {
emitter.on("calcVoice", (res: emitter.EventData) => {
this.calculateAmplitudes(res.data!["buffer"] as ArrayBuffer)
})
}
aboutToDisappear(): void {
emitter.off("calcVoice")
}
3.eventhup 线程内通信
getContext().eventHub.emit('update')
aboutToAppear(): void {
this.getUser()
getContext().eventHub.on('update', () => {
this.getUser()
})
}
emit
HarmonyOS Next中父子通信主要通过@Provide和@Consume装饰器实现。@Provide在父组件声明状态变量并自动同步给子组件,@Consume在子组件接收并使用该状态。状态变更时双向数据绑定自动更新UI。父子组件也可通过自定义事件通信,子组件用this.$emit()触发事件,父组件通过v-on监听处理。