欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 社会 > 从0到1手写实现Event Emitter

从0到1手写实现Event Emitter

2024/12/24 10:43:48 来源:https://blog.csdn.net/zcmartin2014214283/article/details/144672642  浏览:    关键词:从0到1手写实现Event Emitter

1. 引言

在前端开发中,尤其是构建大型应用时,组件之间的通信变得非常复杂。为了实现组件之间的解耦,我们通常会采用事件驱动的方式。事件中心(Event Emitter)机制就是通过集中管理和分发事件来解耦生产者(事件的触发者)和消费者(事件的处理者)。这种机制尤其适合在复杂的前端应用中,减少组件之间的直接依赖。

本文将介绍如何从0到1实现一个简单的事件中心,并用 JavaScript 来构建,适合初学者理解。我们将一步步实现事件中心,并展示如何在实际的前端应用中使用它。

2. 事件中心的基本概念

2.1 事件中心的工作原理

事件中心的核心功能是“订阅”和“发布”事件。生产者通过事件中心发布事件,消费者则通过事件中心订阅感兴趣的事件。事件中心接收到事件时,会将其分发给相应的消费者。

  • 发布事件 :当某个组件发生了某种操作时,它会向事件中心发布一个事件。

  • 订阅事件 :其他组件可以订阅特定类型的事件,当事件发生时,事件中心会通知这些组件。

2.2 事件中心的优势

  • 解耦 :生产者不需要知道谁会处理事件,消费者也不需要知道事件是如何触发的。

  • 提高扩展性 :如果需要增加新的功能或模块,只需要订阅或发布事件,不需要修改现有代码。

  • 提高响应性 :事件可以是异步的,消费者可以在事件到达时进行异步处理,避免了同步执行可能带来的性能问题。

3. 手写实现事件中心

接下来,我们将用 JavaScript 从头开始实现一个简单的事件中心,支持事件的订阅和发布。

3.1 设计事件中心类

我们将首先设计一个 EventEmitter 类,这个类需要提供两个主要的功能:

  • on(eventName, callback):用于订阅事件。

  • emit(eventName, ...args):用于发布事件。

  • off(eventName, ...args):用于取消订阅事件。

我们可以使用 JavaScript 中的对象({})来存储事件类型和对应的回调函数队列。

3.2 代码实现

class EventEmitter {constructor () {this.events = {}}// 订阅事件on (eventName, callback) {if (!this.events[eventName]) {this.events[eventName] = []}this.events[eventName].push(callback)console.log(`Subscribed to event: ${eventName}`);}// 发布事件emit (eventName, ...args) {if (!this.events[eventName]) {return}this.events[eventName].map(callback => callback(...args))console.log(`Event emit: ${eventName}`);}// 取消订阅事件off (eventName, callback) {const eventCallbacks = this.events[eventName]if (!eventCallbacks) returnthis.events[eventName] = eventCallbacks.filter(cb => cb !== callback)console.log(`off event: ${eventName}`);} 
}

3.3 事件中心的使用

下面我们来看一下如何在实际的前端应用中使用这个事件中心。

// 创建一个事件中心实例
const eventEmitter = new EventEmitter();// 定义事件的消费者回调
function onUserLogin(data) {console.log(`User logged in: ${data.username}`);
}function onUserLogout(data) {console.log(`User logged out: ${data.username}`);
}// 订阅事件
eventEmitter.on('userLogin', onUserLogin);
eventEmitter.on('userLogout', onUserLogout);// 发布事件
eventEmitter.emit('userLogin', { username: 'kobe' });
eventEmitter.emit('userLogout', { username: 'jordan' });// 取消订阅
// eventEmitter.off('userLogin', onUserLogin);
eventEmitter.emit('userLogin', { username: 'kobe' }); 


 
我们可以看到在注释掉off事件,可以看到kobe的logged in的信息

// 创建一个事件中心实例
const eventEmitter = new EventEmitter();// 定义事件的消费者回调
function onUserLogin(data) {console.log(`User logged in: ${data.username}`);
}function onUserLogout(data) {console.log(`User logged out: ${data.username}`);
}// 订阅事件
eventEmitter.on('userLogin', onUserLogin);
eventEmitter.on('userLogout', onUserLogout);// 发布事件
eventEmitter.emit('userLogin', { username: 'kobe' });
eventEmitter.emit('userLogout', { username: 'jordan' });// 取消订阅
eventEmitter.off('userLogin', onUserLogin);
eventEmitter.emit('userLogin', { username: 'kobe' });  // 不会触发回调

 

但是当我们正常触发off事件时,就看不到kobe的logged in的信息
 

3.4 解释代码

  • on方法 :用于订阅某个事件。当某个组件对某个事件感兴趣时,它会调用 on方法,传入事件类型和回调函数。事件中心将这个回调函数保存在一个数组中,当事件发布时,会依次执行这些回调函数。

  • emit方法 :用于发布事件。发布者不需要知道谁会处理事件,它只需要向事件中心发布一个事件,并附带数据。事件中心会遍历所有订阅了该事件类型的回调函数并执行它们。

  • off方法 :如果某个组件不再关心某个事件,可以调用 off 方法,取消对该事件的订阅。

4. 事件中心的应用场景

4.1 用户登录/登出事件

在一个大型的前端应用中,用户登录和登出是非常常见的场景。当用户登录或登出时,可能需要执行一些操作(如更新UI、获取用户信息、重定向页面等)。通过事件中心,我们可以将这些操作解耦,避免紧耦合的逻辑。

例如,当用户登录时,我们可以触发 userLogin 事件,其他组件(如用户信息模块、UI模块等)可以订阅这个事件,并在事件触发时执行相应的操作。

4.2 表单提交

在一个表单提交的场景中,用户输入数据并提交表单时,可能会触发多个操作(如数据验证、请求发送、显示加载状态等)。通过事件中心,我们可以将这些操作分开处理,避免在同一个地方处理所有逻辑,简化代码。

4.3 组件间通信

在前端开发中,尤其是单页面应用(SPA)中,多个组件之间经常需要进行通信。通过事件中心机制,组件之间可以通过事件来交换数据,而不需要直接调用其他组件的方法或修改它们的状态。

5. 优化与扩展

5.1 异步处理

我们可以通过异步方式来处理事件,使得事件的消费更加高效。比如可以使用 setTimeoutPromise 来异步执行回调函数,避免阻塞主线程。

5.2 支持一次性订阅

我们可以添加一个一次性订阅功能,使得某些事件只会被触发一次后自动取消订阅。这在某些场景下会非常有用。

once (eventName, callback) {const onceCallback = (...args) => {callback(...args)// 这块注意是onceCallback,而不是callbackthis.off(eventName, onceCallback)// console.log(`once event: ${eventName}`);}this.on(eventName, onceCallback)
}// 调用一下
eventEmitter.once('userLogin', onUserLogin); 
eventEmitter.emit('userLogin', { username: 'durant' });
eventEmitter.emit('userLogin', { username: 'durant' });


 
可以看到user logged in: durant只有一次调用

6. 总结

本文通过从0到1的方式实现了一个简单的事件中心,并讲解了如何在前端应用中使用它来解耦组件之间的通信。事件中心可以帮助我们处理异步事件、提高系统扩展性并简化代码结构,尤其适合用于大型前端应用中的组件间通信。通过本文的代码实现,相信你已经能掌握事件中心的基本原理和使用方法,并能在实际项目中加以应用。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com