1. 前言
1.1 环境
const emitter = mitt()
// listen to an event emitter.on(‘foo’, e => console.log(‘foo’, e) )
// listen to all events emitter.on(‘*’, (type, e) => console.log(type, e) )
// fire an event emitter.emit(‘foo’, { a: ‘b’ })
// clearing all events emitter.all.clear()
// working with handler references: function onFoo() {} emitter.on(‘foo’, onFoo) // listen emitter.off(‘foo’, onFoo) // unlisten
<a name="ow0sa"></a>## 2.2 mitt 源码```javascript/** ts声明,非逻辑代码 start **/// 事件类型是字符串或者symbolexport type EventType = string | symbol;// An event handler can take an optional event argument// and should not return a value// 传入事件export type Handler<T = unknown> = (event: T) => void;// 通配符处理,传入类型和事件export type WildcardHandler<T = Record<string, unknown>> = (type: keyof T,event: T[keyof T]) => void;// An array of all currently registered event handlers for a type// 事件回调数组export type EventHandlerList<T = unknown> = Array<Handler<T>>;// 通配符事件回调数组export type WildCardEventHandlerList<T = Record<string, unknown>> = Array<WildcardHandler<T>>;// A map of event types and their corresponding event handlers.// 事件类型和事件回调列表export type EventHandlerMap<Events extends Record<EventType, unknown>> = Map<keyof Events | '*',EventHandlerList<Events[keyof Events]> | WildCardEventHandlerList<Events>>;export interface Emitter<Events extends Record<EventType, unknown>> {all: EventHandlerMap<Events>;// ts的重载,事件注册,可以传type也可以传*,进入到不同的handler回调on<Key extends keyof Events>(type: Key, handler: Handler<Events[Key]>): void;on(type: '*', handler: WildcardHandler<Events>): void;// 事件解绑off<Key extends keyof Events>(type: Key, handler?: Handler<Events[Key]>): void;off(type: '*', handler: WildcardHandler<Events>): void;// 事件发布emit<Key extends keyof Events>(type: Key, event: Events[Key]): void;emit<Key extends keyof Events>(type: undefined extends Events[Key] ? Key : never): void;}/** ts声明,非逻辑代码 end **//*** Mitt: Tiny (~200b) functional event emitter / pubsub.* @name mitt* @returns {Mitt}*/export default function mitt<Events extends Record<EventType, unknown>>(all?: EventHandlerMap<Events>): Emitter<Events> {// type类型type GenericEventHandler =| Handler<Events[keyof Events]>| WildcardHandler<Events>;// all可以传入map或者创建一个mapall = all || new Map();return {// type名称合集/*** A Map of event names to registered handler functions.*/all,/*** Register an event handler for the given type.* @param {string|symbol} type Type of event to listen for, or `'*'` for all events* @param {Function} handler Function to call in response to given event* @memberOf mitt*/// 事件注册, 事件名和回调函数on<Key extends keyof Events>(type: Key, handler: GenericEventHandler) {// 事件名的列表是否存在,存在直接从all中getconst handlers: Array<GenericEventHandler> | undefined = all!.get(type);// 存在该事件名列表,直接pushif (handlers) {handlers.push(handler);}// 不存在,设置一个新的 {type: [handler]}else {all!.set(type, [handler] as EventHandlerList<Events[keyof Events]>);}},/*** Remove an event handler for the given type.* If `handler` is omitted, all handlers of the given type are removed.* @param {string|symbol} type Type of event to unregister `handler` from, or `'*'`* @param {Function} [handler] Handler function to remove* @memberOf mitt*/// 事件解绑,事件名,回调函数选传off<Key extends keyof Events>(type: Key, handler?: GenericEventHandler) {// 查看是否存在该事件列表const handlers: Array<GenericEventHandler> | undefined = all!.get(type);if (handlers) {// 如果传入了回调函数,只从回调列表中删除这一个函数,其他的保留if (handler) {// >>> 这个操作符就像>>操作符一样,只是左移的位总是零。// >>> 不太会用,只知道大于等于0都是原来的值,如果小于0,是一个非常大的数字,基本越界,超出了handlers的范围handlers.splice(handlers.indexOf(handler) >>> 0, 1);}else {// 清空该回调列表all!.set(type, []);}}},/*** Invoke all handlers for the given type.* If present, `'*'` handlers are invoked after type-matched handlers.** Note: Manually firing '*' handlers is not supported.** @param {string|symbol} type The event type to invoke* @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler* @memberOf mitt*/// 事件发布emit<Key extends keyof Events>(type: Key, evt?: Events[Key]) {// 获取事件回调列表let handlers = all!.get(type);if (handlers) {(handlers as EventHandlerList<Events[keyof Events]>)// 创建一个新的列表.slice().map((handler) => {handler(evt!);});}// 获取属于* 的回调列表handlers = all!.get('*');if (handlers) {(handlers as WildCardEventHandlerList<Events>).slice().map((handler) => {// 传入事件类型和值handler(type, evt!);});}}};}
2.3 tiny-emitter 使用
var Emitter = require('tiny-emitter');var emitter = new Emitter();emitter.on('some-event', function (arg1, arg2, arg3) {//});emitter.emit('some-event', 'arg1 value', 'arg2 value', 'arg3 value');
2.4 tiny-emitter 源码
// 这个写法不太推荐,声明了一个无语义化的构造函数function E () {// Keep this empty so it's easier to inherit from// (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)}E.prototype = {// 事件注册 名称,回调,上下文on: function (name, callback, ctx) {// 这个e相当于mitt的map对象,存储事件和回到函数var e = this.e || (this.e = {});// 简写,当列表存在和不存在直接赋值[](e[name] || (e[name] = [])).push({fn: callback,ctx: ctx});return this;},// 事件注册,只执行依次once: function (name, callback, ctx) {var self = this;// 回调函数function listener () {// 执行回调时,解绑这一个回调,其他相同的事件名称还是可以监听到发布的消息self.off(name, listener);// 执行回调函数callback.apply(ctx, arguments);};// 在listener上在绑定了回调函数,可能有些处理用吧,怪异的写法listener._ = callbackreturn this.on(name, listener, ctx);},// 事件发布emit: function (name) {// 支持数据多参数,将name这个参数去除了,这里是和mitt的差异点var data = [].slice.call(arguments, 1);// 生成新的数组,不修改原数组var evtArr = ((this.e || (this.e = {}))[name] || []).slice();var i = 0;var len = evtArr.length;// 循环执行所有的回调事件for (i; i < len; i++) {evtArr[i].fn.apply(evtArr[i].ctx, data);}return this;},// 事件解绑,这里的回调函数必传,不支持批量删除所有的事件,这里是和mitt的差异点off: function (name, callback) {var e = this.e || (this.e = {});var evts = e[name];var liveEvents = [];// 回调函数需要传的情况,删除这个函数if (evts && callback) {for (var i = 0, len = evts.length; i < len; i++) {if (evts[i].fn !== callback && evts[i].fn._ !== callback)liveEvents.push(evts[i]);}}// Remove event from queue to prevent memory leak// Suggested by https://github.com/lazd// Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910// 如果所有的回调事件都解绑了,直接删除事件列表(liveEvents.length)? e[name] = liveEvents: delete e[name];return this;}};module.exports = E;module.exports.TinyEmitter = E;
3. 总结
- mitt使用documentation自动生成文档,基于注释注释
这个语法可以用在indexOf配合splice处理,小于0基本会越界,不会删除数组中的值,如-1 >>>0 等于4294967295,依次往上加1
- mitt使用microbundle打包cjs,umd,mjs
- tiny-emitter 使用 browserify,没有microbundle的好用,需要配置的东西比较多
- tiny-emitter 和 mitt在发布订阅机制上逻辑是相同的,区别 | | mitt | tiny-emitter | | —- | —- | —- | | emit支持传递2个以上参数 | 不支持 | 支持 | | off是否支持全量解绑 | 支持 | 不支持 | | once只执行一次 | 不支持 | 支持 | | 是否支持监听所有事件* | 支持 | 不支持 | | 是否可以从外部传入事件map列表 | 支持 | 不支持 |
