文章

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 进行授权