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 功能的事件总线),也可以继续优化升级。

相关推荐
zhanshuo3 小时前
构建可扩展的状态系统:基于 ArkTS 的模块化状态管理设计与实现
harmonyos
codefish7987 小时前
鸿蒙开发学习之路:从入门到实践的全面指南
harmonyos
yrjw13 小时前
一款基于react-native harmonyOS 封装的【文档】文件预览查看开源库(基于Harmony 原生文件预览服务进行封装)
harmonyos
搜狐技术产品小编20231 天前
搜狐新闻直播间适配HarmonyOs实现点赞动画
华为·harmonyos
zhanshuo1 天前
ArkUI 玩转水平滑动视图:超全实战教程与项目应用解析
harmonyos·arkui
zhanshuo1 天前
ArkUI Canvas 实战:快速绘制柱状图图表组件
harmonyos·arkui
zhanshuo2 天前
手把手教你用 ArkUI 写出高性能分页列表:List + onScroll 实战解析
harmonyos
zhanshuo2 天前
深入解析 ArkUI 触摸事件机制:从点击到滑动的开发全流程
harmonyos
i仙银2 天前
鸿蒙沙箱浏览器 - SandboxFinder
app·harmonyos