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 **/
// 事件类型是字符串或者symbol
export 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或者创建一个map
all = 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中get
const handlers: Array<GenericEventHandler> | undefined = all!.get(type);
// 存在该事件名列表,直接push
if (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._ = callback
return 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列表 | 支持 | 不支持 |