ArkTS 模块通信全解析:用事件总线实现页面消息联动

摘要

在 HarmonyOS 的开发过程中,随着项目模块的不断增加,页面与组件之间的直接调用变得不再现实。如何高效、解耦地实现模块间通信,成为了开发者关注的重点。ArkTS 提供了多种方案,其中事件总线是一种简单、轻便、扩展性强的方式,适用于大部分跨模块通信场景。

引言

在日常开发中,我们常常会遇到这样的情况:A 模块的某个动作需要影响 B 模块的状态,但这两个模块又没有直接的父子或引用关系。如果你还在用全局变量、静态方法传值,那其实风险挺大 ------ 不仅维护难度高,而且逻辑容易混乱。为了解耦模块之间的通信,我们可以借助事件总线(EventBus)

事件总线是一种发布-订阅模式,它允许我们在不同组件或模块之间传递消息,而不需要彼此知道对方的存在。

跨模块通信的实现方式

使用事件总线封装通信逻辑

我们先来创建一个全局的事件总线类 EventBus.ts,它是整个通信机制的核心。

ts 复制代码
// EventBus.ts
export class EventBus {
  private static instance: EventBus;
  private events: { [key: string]: Function[] } = {};

  private constructor() {}

  static getInstance() {
    if (!EventBus.instance) {
      EventBus.instance = new EventBus();
    }
    return EventBus.instance;
  }

  on(event: string, callback: Function) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(callback);
  }

  emit(event: string, data?: any) {
    if (this.events[event]) {
      this.events[event].forEach((callback) => callback(data));
    }
  }
}

这个类使用了单例模式,确保事件中心只有一个实例,并通过 on 方法监听事件,通过 emit 方法广播事件。

主页面:发送事件的模块

我们在主页面 MainPage.ets 中使用按钮点击来发送一个事件:

ts 复制代码
// MainPage.ets
import { EventBus } from './EventBus';

@Entry
@Component
struct MainPage {
  build() {
    Column() {
      Button('Send Event')
        .onClick(() => {
          EventBus.getInstance().emit('message', 'Hello from MainPage');
        });
    }.padding(20);
  }
}

点击按钮时,通过 emit 方法发送了一个名为 message 的事件,并携带数据 Hello from MainPage

接收页面:监听并响应事件

在另一个页面 SecondaryPage.ets 中,我们来接收这个事件:

ts 复制代码
// SecondaryPage.ets
import { EventBus } from './EventBus';

@Entry
@Component
struct SecondaryPage {
  private message: string = 'No message yet';

  build() {
    // 注册事件监听器
    EventBus.getInstance().on('message', (data) => {
      this.message = data;
    });

    Column() {
      Text(this.message)
        .fontSize(20);
    }.padding(20);
  }
}

SecondaryPage 中注册了 message 事件的监听器,每当有消息发出时,就会更新页面上的 message

实际应用场景举例

场景一:登录成功后通知首页刷新用户信息

代码示例:

ts 复制代码
// LoginPage.ets
EventBus.getInstance().emit('loginSuccess', { username: '张三' });

// HomePage.ets
EventBus.getInstance().on('loginSuccess', (data) => {
  this.username = data.username;
});

说明:

用户登录成功后不需要手动跳转或回调处理,首页收到 loginSuccess 事件后即可自动更新展示用户名或用户信息。

场景二:设置页面修改主题,通知多个模块更新 UI 风格

代码示例:

ts 复制代码
// SettingsPage.ets
EventBus.getInstance().emit('themeChanged', 'dark');

// AnyPage.ets
EventBus.getInstance().on('themeChanged', (theme) => {
  this.currentTheme = theme;
});

说明:

通过事件总线广播主题变更事件,多个页面只需要注册监听就能自动响应,无需层层传递参数。

场景三:播放器状态通知多个页面同步更新

代码示例:

ts 复制代码
// Player.ets
EventBus.getInstance().emit('playStatus', 'paused');

// FooterWidget.ets / LyricsPage.ets / ControlPanel.ets
EventBus.getInstance().on('playStatus', (status) => {
  this.playStatus = status;
});

说明:

当播放器状态改变后,通过事件总线通知相关组件更新播放图标、歌词状态、控制栏等,做到统一同步更新。

QA 环节

Q1: 为什么不直接使用全局变量或传参来实现?

全局变量虽然方便,但维护困难、安全性差,容易导致模块耦合度过高。事件总线属于发布-订阅模式,发送者和接收者解耦,维护更清晰,扩展性强。

Q2: 如果页面被销毁了,事件监听器还存在吗?

当前实现中,监听器会一直保留在内存中。如果你在组件卸载后不需要再接收事件,建议添加手动移除监听器的功能(可扩展 off 方法)来避免内存泄漏。

Q3: 事件可以传复杂对象吗?

可以,emit(event: string, data: any) 中的 data 可以是字符串、对象、数组甚至函数,只要发送和接收逻辑一致即可。

总结

事件总线是一种非常实用的跨模块通信机制,特别适用于页面之间、组件之间没有直接引用关系时的消息传递。在 ArkTS 的实际开发中,它既轻便,又高效,能大大减少模块之间的耦合,提高代码的可维护性。

如果你在做分布式模块化开发、复杂页面联动,或者需要组件间通信,不妨试试这个思路。

如需后续支持场景(例如带 onceoff 功能的事件总线),也可以继续优化升级。

相关推荐
爱笑的眼睛111 天前
深入剖析 HarmonyOS ArkUI 声明式开发:状态管理艺术与最佳实践
华为·harmonyos
安卓开发者1 天前
鸿蒙NEXT交互机制解析:从输入设备到手势响应的全面指南
microsoft·交互·harmonyos
奶糖不太甜。1 天前
鸿蒙分布式数据同步失败全解
分布式·华为·harmonyos·数据同步
小小小小小星2 天前
鸿蒙权限相关问题之应用访问网络、文件等功能时崩溃或无响应,日志提示'权限被拒绝'
harmonyos
leon_teacher2 天前
ArkUI核心功能组件使用
android·java·开发语言·javascript·harmonyos·鸿蒙
lisir92 天前
鸿蒙手势实战解析+手势冲突解决策略总结
harmonyos
安卓开发者2 天前
鸿蒙Next图形绘制指南:从基础几何图形到复杂UI设计
ui·华为·harmonyos
开发小能手嗨啊3 天前
鸿蒙开发进阶(HarmonyOS)
harmonyos·鸿蒙·鸿蒙开发·开发教程·纯血鸿蒙·南向开发·北向开发
大土豆的bug记录3 天前
鸿蒙总改变字体大小设置
华为·harmonyos
我爱学习_zwj3 天前
【鸿蒙面试题-6】LazyForEach 懒加载
华为·harmonyos