React15 - 如何在React 15中实现自定义的事件订阅与发送(例如组件间通信)
在React 15中实现组件间的自定义事件通信,通常采用"发布-订阅"(Pub-Sub)模式。这种模式能让你摆脱繁琐的props层层传递,让任意两个组件直接"对话"。根据你的项目需求,主要有三种实现方式,我按推荐程度逐一介绍。
🎯 方案一:使用专门的库(如mitt)
这是最推荐的方式,特别适合中等以上复杂度的项目。专门的库通常体积小、API友好,并且已经帮你处理好了细节。
-
安装:在项目中安装mitt库。
npm install mitt # 或者 yarn add mitt -
创建事件总线:新建一个文件(如
eventBus.js),创建并导出一个mitt实例,它就成为了我们全局的"通信中心"。import mitt from "mitt"; const emitter = mitt(); export default emitter; -
在组件中"订阅"事件(接收方):在需要接收消息的组件中,通常在
componentDidMount(类组件)或useEffect(函数组件)中监听事件。关键:在componentWillUnmount中移除监听,防止内存泄漏。// 接收方组件 import React, { useEffect } from "react"; import emitter from "./eventBus"; const ReceiverComponent = () => { useEffect(() => { // 定义处理函数 const handleMessage = (data) => { console.log("收到消息:", data); // 更新组件状态或执行其他操作 }; // 订阅 'message' 事件 emitter.on("message", handleMessage); // 组件卸载时取消订阅 return () => { emitter.off("message", handleMessage); }; }, []); return <div>等待消息...</div>; }; -
在组件中"发布"事件(发送方):在需要发送消息的组件中,通过调用
emit方法触发事件,并传递数据。// 发送方组件 import React from "react"; import emitter from "./eventBus"; const SenderComponent = () => { const sendMessage = () => { // 发布 'message' 事件,并携带数据 emitter.emit("message", "你好,接收方!"); }; return <button onClick={sendMessage}>发送消息</button>; };
🧱 方案二:手写一个简单的Event Bus
如果你希望不引入任何外部依赖,手写一个简单的Event Bus是很好的轻量级方案。
-
创建Event Bus工具:新建
eventBus.js,实现一个简单的发布-订阅类。// eventBus.js class EventBus { constructor() { this.events = {}; // 用于存储事件名和对应的回调函数列表 } // 订阅事件 subscribe(eventName, callback) { if (!this.events[eventName]) { this.events[eventName] = []; } this.events[eventName].push(callback); // 返回一个取消订阅的函数 return () => { this.events[eventName] = this.events[eventName].filter( (fn) => fn !== callback, ); }; } // 发布事件 publish(eventName, data) { if (this.events[eventName]) { this.events[eventName].forEach((callback) => callback(data)); } } } const eventBus = new EventBus(); export default eventBus; -
在组件中使用:使用方式与mitt类似,通过
eventBus.subscribe监听,eventBus.publish触发。// 接收方 import React, { useEffect } from "react"; import eventBus from "./eventBus"; function Receiver() { useEffect(() => { const unsubscribe = eventBus.subscribe("userLogin", (userData) => { console.log("用户登录:", userData); }); return unsubscribe; // 清理订阅 }, []); // ... } // 发送方 function Sender() { const login = () => { eventBus.publish("userLogin", { name: "Alice", id: 123 }); }; // ... }
🌐 方案三:利用浏览器原生API (EventTarget)
这个方案利用了浏览器内置的EventTarget API和CustomEvent,无需额外安装库,也无需手写复杂逻辑,是一种现代、轻量且原生的方式。
-
创建事件总线:直接创建一个
EventTarget实例作为中央总线。// eventBus.js const eventTarget = new EventTarget(); export default eventTarget; -
订阅事件:使用
addEventListener监听自定义事件,并通过event.detail获取传递的数据。// 接收方组件 import React, { useEffect } from "react"; import eventTarget from "./eventBus"; const Receiver = () => { useEffect(() => { const handleMessage = (event) => { console.log("收到:", event.detail); // 数据在 detail 属性中 }; eventTarget.addEventListener("message", handleMessage); return () => { eventTarget.removeEventListener("message", handleMessage); }; }, []); // ... }; -
发布事件:使用
dispatchEvent方法,并创建一个CustomEvent对象,通过其detail属性携带数据。// 发送方组件 import React from "react"; import eventTarget from "./eventBus"; const Sender = () => { const sendMessage = () => { const event = new CustomEvent("message", { detail: "Hello from EventTarget!", }); eventTarget.dispatchEvent(event); }; return <button onClick={sendMessage}>发送</button>; };
💎 总结与建议
以上三种方式都能实现你需要的自定义事件通信。它们如何选择,可以参考这个思路:
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| mitt库 | 功能完善,API友好,体积超小(200字节),社区成熟 | 需要额外安装依赖 | 推荐用于大多数项目,特别是需要可靠、简洁方案时 |
| 手写Event Bus | 无依赖,完全掌控,理解原理 | 需要自己处理边界情况,代码稍多 | 学习目的,或项目有严格的依赖限制 |
| 原生EventTarget | 无依赖,利用浏览器内置API,现代、简洁 | 数据需要放在detail属性里,写法稍显啰嗦 |
喜欢原生API,不想引入任何库或手写维护代码的场景 |
另外,无论选择哪种方式,一个重要的原则是:在组件卸载时务必取消订阅,这是避免内存泄漏和诡异bug的关键。

浙公网安备 33010602011771号