ArkTS 组件通信机制深度解析
ArkTS 组件通信:@Prop/@Link 父子传值、EventHub、AppStorage 全局状态与自定义事件。 讲清各通信方式边界,掌握后能答出「鸿蒙组件间怎么共享数据」。
ArkTS 组件通信机制深度解析
一句话概括
ArkTS 组件通信 = 父子走装饰器(@Prop/@Link + 回调)→ 跨层级走 Provide/Consume → 全局走 EventHub 事件总线 / AppStorage,选对通信方式的核心原则是:耦合度与通信范围成正比。
核心知识点
1. 父子通信——@Prop + 回调,最标准解耦方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Component
struct TaskCard {
@Prop task: TaskData;
onToggle?: (id: number) => void; // 子→父:通过回调,不是直接改父状态
build() {
Row() {
Checkbox({ isSelected: this.task.completed })
.onChange(() => this.onToggle?.(this.task.id)) // 通知父组件处理
Text(this.task.title)
}
}
}
// 原则:子组件不直接修改父的数据,通过回调把"意图"上报
// 父组件决定如何处理——修改自己的 @State
2. EventHub——解耦最好的跨组件通信
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 组件 A 发送事件
getContext().eventHub.emit('userLogout', { userId: 123 });
// 组件 B 监听事件(在 aboutToAppear 中注册,aboutToDisappear 中取消)
aboutToAppear() {
this.eventHub.on('userLogout', (data) => {
this.clearUserCache();
});
}
aboutToDisappear() {
this.eventHub.off('userLogout'); // ⚠️ 必须取消,否则内存泄漏
}
// EventHub 是页面级别的,作用范围 = 当前页面 UIAbility 内的所有组件
// 适用于:登录/登出通知、主题切换、全局刷新等松耦合场景
3. AppStorage——应用级持久化共享
1
2
3
4
5
6
7
8
9
10
11
// 存(任意页面/组件)
AppStorage.setOrCreate('isLoggedIn', true);
AppStorage.setOrCreate('userName', '张三');
// 取(任意页面/组件,支持响应式绑定)
@StorageLink('isLoggedIn') isLoggedIn: boolean = false;
@StorageProp('userName') userName: string = '';
// StorageLink = 双向(改了就写入 AppStorage)
// StorageProp = 单向(只读快照)
// AppStorage 存到内存,杀进程丢失;需要持久化用 PersistentStorage
4. LocalStorage——页面级状态隔离
1
2
3
4
// 页面 A 创建自己的 Storage
let storage = new LocalStorage({ 'pageData': {} });
// 每个页面独立,互不干扰
// 场景:多 Tab 页各自维护状态,退出页面自动释放
5. 选型总结
| 场景 | 推荐方式 | 为什么 |
|---|---|---|
| 父→子传展示数据 | @Prop | 最简单,单向安全 |
| 子→父回写数据 | 回调函数 | 解耦,子不持有父引用 |
| 双向实时同步 | @Link | 但要慎用,耦合度高 |
| 跨 3+ 层透传 | @Provide/@Consume | 避免中间层 props 地狱 |
| 不相关组件通知 | EventHub | 发布-订阅,最松耦合 |
| 全局配置/登录态 | AppStorage | 任意页面可读写 |
其实你每天都在用
- 列表项点击:TaskCard 的 onToggle 回调 → 父组件收到 id → 更新对应 task
- 登录状态:登录页设置
AppStorage.setOrCreate('token', ...)→ 其他所有页面 @StorageLink 感知变化 - 退出登录:事件总线
eventHub.emit('logout')→ 所有监听者同时清缓存、跳转登录页 - Tab 切换:@Provide tabIndex → @Consume 在深层子组件控制高亮
- 表单页:@Link 双向绑定每个字段,提交时统一读取
常见误解(FAQ)
❌ 误区:「EventHub 是全局的」 EventHub 作用域是页面级别(UIAbility 内),不是你想象的 app 全局。跨 Ability 通信要用 Emitter(系统级事件)或 AppStorage。
❌ 误区:「@Link 是子改父变,反过来不会」 @Link 是双向的。父组件改了绑定的 @State,子组件也会跟着刷新。不是单向的”子汇报给父”。
❌ 误区:「AppStorage 和 PersistentStorage 是一回事」 AppStorage 存内存(杀进程就丢),PersistentStorage 持久化到磁盘。且 PersistentStorage 的值变化会自动同步到 AppStorage,所以绑定 @StorageLink 一样响应。
❌ 误区:「子组件直接改 @Prop 就行,反正只有一个用途」 @Prop 是值拷贝,你改了子组件内的副本不但不会影响父,而且框架不会因为这个变化重建 UI——因为你改的是”不受管理的变量”。正确姿势是回调。
一句话总结
选择通信方式的核心准则:离得近用装饰器,离得远用总线,全局共享用 Storage——别让一个组件的实现细节污染另一个组件的状态。
本文由作者按照 CC BY 4.0 进行授权