mitt tiny-emitter发布订阅应用场景源码解析
简介
mitt
是一个基于JavaScript的简单、快速、可扩展的发布/订阅(pub/sub)库,适用于各种应用场景。它的基本思想是订阅者向一个发布者注册其感兴趣的事件类型,当该类型事件发生时,订阅者会被通知并执行其所定义的响应逻辑。这种解耦合的模式为开发者提供了良好的可维护性和扩展性。
mitt
的实现采用了只有500多个字节的精简代码,同时它还具有优雅和易用的API,因此备受广大开发者的喜爱。本文将对mitt
的应用场景和源码进行详细介绍,并通过两个具体示例进行说明。
应用场景
mitt
适用于各种应用场景,主要包括以下几个方面:
- 事件代理:
mitt
可以用于事件代理,即将子组件通知/通信的事件发送到它们的父组件。这样可以很好地解耦,方便组件系统的维护和扩展。 - 状态管理:
mitt
可以用于状态管理,即当状态改变时,它可以通知所有与之相关的订阅者,从而实现组件之间的协作、同步和响应。 - 异步/回调处理:
mitt
可以用于异步/回调处理,即在异步/回调方法中使用mitt
来处理完成事件,从而在异步操作完成后进行相应的处理。
源码解析
下面是mitt
的源码解析。首先是mitt
的主体代码:
export default function mitt(all = Object.create(null)) {
return {
/**
* Register an event handler for the given type.
*
* @param {string} type Type of the event to listen for, or `"*"` for all events
* @param {Function} handler Function to call in response to the given event
* @memberOf mitt
*/
on(type, handler) {
(all[type] || (all[type] = [])).push(handler);
},
/**
* Remove an event handler for the given type.
*
* @param {string} type Type of the event to unregister `handler` from, or `"*"`
* @param {Function} handler Handler function to remove
* @memberOf mitt
*/
off(type, handler) {
if (all[type]) {
all[type].splice(all[type].indexOf(handler) >>> 0, 1);
}
},
/**
* Invoke all handlers for the given type.
* If present, `"*"` handlers are invoked after type-matched handlers.
*
* @param {string} type The event type to invoke
* @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler
* @memberOf mitt
*/
emit(type, evt) {
(all[type] || []).slice().map((handler) => { handler(evt); });
(all['*'] || []).slice().map((handler) => { handler(type, evt); });
}
};
}
mitt
函数接收一个名为all
的对象作为参数,它用来存储事件类型和其对应的处理函数。具体的实现中,all
使用Object.create(null)
生成,它是一个全新的、空的对象,没有原型链的干扰。
mitt
函数返回一个带有3个方法的对象,分别为on
、off
和emit
:
on(type, handler)
:用于为指定类型的事件注册一个处理函数。off(type, handler)
:用于删除指定类型的事件的处理函数。emit(type, evt)
:用于触发指定类型的事件,并可传递相关数据。
下面是对每个方法的具体实现进行说明:
on(type, handler)
这个方法用于注册事件处理函数。它接收两个参数,分别为事件类型和处理函数。如果事件类型已经存在,则在其对应的处理函数数组中添加新处理函数;如果事件类型不存在,则创建新的处理函数数组,添加处理函数并保存至all
对象中。代码如下:
(all[type] || (all[type] = [])).push(handler);
要注意的是,这里有一个判断条件(all[type] || (all[type] = []))
,如果all
对象没有type
属性,那么就会将其初始化为一个空数组。
off(type, handler)
这个方法用于删除事件处理函数。它接收两个参数,分别为事件类型和处理函数。如果事件类型的处理函数数组存在,且其中包含指定的处理函数,则将其从数组中删除。代码如下:
if (all[type]) {
all[type].splice(all[type].indexOf(handler) >>> 0, 1);
}
要注意的是,这里使用了位运算符>>>
,它的作用是将其后面的数字转换为无符号32位整数。如果不使用位运算符,则在处理器函数未找到时,其返回值为-1,会导致数组删除失效。
emit(type, evt)
这个方法用于触发指定类型的事件。它接收两个参数,分别为事件类型和传递给事件处理函数的数据。首先,它将all
对象中记录的该事件类型的所有处理函数复制到一个新数组中,然后依次执行每一个数组元素的处理函数。代码如下:
(all[type] || []).slice().map((handler) => { handler(evt); });
其次,如果存在all
对象的'*'
属性,则将包含在'*'
中的处理函数也添加到新数组中。代码如下:
(all['*'] || []).slice().map((handler) => { handler(type, evt); });
这样,所有处理函数都将被执行,而且'*'
中的处理函数将排在普通类型处理函数的后面。
示例说明
示例1:组件通信
在Vue组件中使用mitt
实现子组件向父组件通信。
<template>
<div>
<Child @notify="onNotify"></Child>
<p v-if="message">{{ message }}</p>
</div>
</template>
<script>
import mitt from 'mitt';
const emitter = mitt();
export default {
data() {
return {
message: ''
};
},
methods: {
onNotify(data) {
this.message = data;
}
},
mounted() {
emitter.on('notify', this.onNotify);
},
beforeDestroy() {
emitter.off('notify', this.onNotify);
}
};
</script>
在子组件中,我们可以通过emitter.emit('notify', data)
将数据传递到父组件中。
<template>
<button @click="onClick">点击通知</button>
</template>
<script>
import mitt from 'mitt';
const emitter = mitt();
export default {
methods: {
onClick() {
// 通知父组件
emitter.emit('notify', 'Hello, Parent Component!');
}
}
};
</script>
示例2:异步回调处理
通过mitt
来处理异步方法的回调,例如Vue
官方提供的$http
方法。
// 使用Vue
this.$http.get('api/data').then((res) => {
emitter.emit('data', res.data);
});
// 注册处理函数
emitter.on('data', (data) => {
// 处理数据
});
或者使用其他HTTP库。
// 使用axios
axios.get('api/data').then((res) => {
emitter.emit('data', res.data);
});
// 注册处理函数
emitter.on('data', (data) => {
// 处理数据
});
这样,在异步操作完成后,我们就可以通过emit
方法同步数据,实现逻辑的处理。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:mitt tiny-emitter发布订阅应用场景源码解析 - Python技术站