mitt 上手教程
Published onAugust 03, 2025
-Views
5Minutes Read
是一个非常轻量级的事件发射器(Event Emitter),它的核心思想是提供一个简单的发布/订阅(Pub/Sub)模式,让你可以轻松地在应用程序的不同部分之间进行通信,而无需它们直接相互依赖。
一、基础用法:创建、监听与触发
的 API 非常直观,主要围绕以下三个核心方法:、 和 。
1. 创建事件总线 (Emitter)
实际上是一个工厂函数,它返回一个 实例。
2. 监听事件 (on)
使用 方法来订阅一个事件。
它接收两个参数:事件名(字符串)和回调函数。当事件被触发时,回调函数就会被执行。
这一点类似 jQuery 的事件监听,不过事件名是可以自由定义的。
3. 触发事件 (emit)
使用 方法来发布或触发一个事件。它接收两个参数:事件名(字符串)和可选的事件数据。事件数据可以是你想要的任何类型,比如对象、字符串或数字。
那么在上一节的「监听 'foo' 事件」代码中就会打印:
4. 移除监听器 (off)
当某个组件不再需要监听事件时,你可以使用 方法来移除监听器。 也需要传入事件名和回调函数。
以 Vue3 代码为例, 在组件卸载前通常需要移除监听器:
传入的回调函数必须是监听时使用的同一个函数引用,否则无法移除。这一点和 JS 的 addEventListener 类似。
二、进阶用法:更强大的功能与实践模式
1. 监听所有事件
监听所有事件: 支持使用 作为事件名来监听所有被触发的事件。你的回调函数会接收到两个参数: 和 。
2. 移除所有监听器
当你只传入第一个参数(事件名)时,所有与该事件名关联的监听器都会被移除。
的 方法是一个重载函数,它的行为取决于你传入的参数数量:
- :移除该 事件的所有监听器。
- emitter.off(type, handler)typehandler` 监听器。
你还可以使用 方法,一次性清除所有事件的所有监听器。
实际上是一个 类型,因此可以使用 和 以及其他 实例的方法: Map - JavaScript | MDN。
3. 命名空间: 通过事件命名规范实现轻量级隔离
在只使用一个 实例时,你可以通过为事件名添加前缀来创建“命名空间”,从而避免事件冲突和增强可读性。
命名方式建议使用冒号或斜杠分隔符,如 、,核心思想是: + 。
例如:
使用命名空间后,你可以利用字符串操作来做一些灵活的批量处理。 例如,如果你想监听所有与用户相关的事件,你只需要在监听器中判断事件名是否以 开头即可。
这种模式让你在同一个 实例上,也能实现类似分组的效果,大大提升了事件处理的灵活性。
总的来说,命名空间的方式有三个好处:
- 避免冲突,增强代码健壮性
- 提高可读性,一眼看懂事件来源
- 实现批量操作和灵活筛选
但缺点也很明显,那就是命名空间是一种人为的约束,依赖于团队的纪律性。
4. 创建多个独立实例:实现物理隔离
在大型或复杂的应用中,为不同的功能模块创建独立的 实例是更好的选择。这是一种代码层面的强制隔离,更安全、更健壮。
直接为不同模块创建单独的 实例,可以让你更好地控制事件的范围和范围内的通信。
然后在不同模块中,只需要导入对应的 实例,就可以进行事件的发布和订阅。
不同的 实例之间是完全隔离的,互不影响。
三、记得在适当时机接触监听
为什么需要解除监听?
如果不解除监听,可能会导致以下两个主要问题:
1. 内存泄漏 (Memory Leak)
当你为一个事件添加了监听器后,这个监听器就会被事件总线( 实例)所“持有”。如果你的组件被销毁(例如,用户从一个页面跳转到另一个页面),但你没有移除这个监听器,事件总线仍然会保留对该组件回调函数的引用。
这意味着即使组件的 DOM 元素和数据已经被垃圾回收器清除,这个回调函数及其闭包中引用的所有数据(包括组件实例本身)依然无法被回收。随着用户在应用中不断切换页面,越来越多的废弃监听器会堆积在内存中,最终导致应用程序的性能下降,甚至崩溃。
2. 意料之外的行为和 bug
假设你有一个组件 A,它监听了 事件。当用户登录时,组件 A 会执行某个操作。
想象下面的场景:
- 你进入了组件 A 所在的页面。
- 你离开了这个页面,组件 A 被销毁了。
- 你又重新进入了组件 A 所在的页面,一个新的组件 A 实例被创建。
- 现在,事件总线上实际上有两个 的监听器:一个来自旧的、已销毁的组件,另一个来自新的组件。
- 当用户登录时,这个事件会被触发两次,导致你的逻辑被重复执行,从而产生难以追踪的 bug。
示例说明
下面是关键代码:
如果是 react 项目,可以在 useEffect 的回调函数中清除监听:
四、结语
是一个非常轻量级的事件发射器,它提供了一种简单、灵活的发布/订阅模式,可以帮助你在应用程序的不同部分之间进行通信。
它的 API 非常直观,使用起来也很方便,但它也有一些局限性,比如命名空间的约束、批量操作的不便,而且大量使用 会增加代码的复杂度,增加维护难度,并且难以跟踪和管理,从长期来看维护起来会让人头疼。
不过,如果你有更复杂的需求,比如需要实现更高级的功能,或者需要更灵活的架构, 也是一个不错的选择。
Tags:
#Mitt