前言
本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。
**这是源码共读的第8期,链接:**juejin.cn/post/708498…
发布订阅模式简介
发布订阅是一种设计模式,包含发布者、订阅者和中间平台三者
- 发布者:负责发送消息到特定的主题,不关心谁接收消息
- 订阅者:订阅一个或多个主题,只接收感兴趣的的消息
- 中间平台:中间组件,负责管理主题的订阅关系、消息的分发
例如Vue的EventBus就使用了发布订阅,通过
$emit(发布)和$on(订阅)传递消息
// eventBus就是一个收集事件的中间平台
const eventBus = new Vue()
// 发布者
eventBus.$emit('message', 'hi message')
// 订阅者
eventBus.$on('message', (data) => {
console.log(data)
})
mitt简单使用
import mitt from 'mitt'
const emitter = mitt()
// 监听订阅一个事件
emitter.on('go', e => console.log('go go go', e) )
// 触发一个事件
emitter.emit('go', 'hi, im a message')
// 清空所有的事件
emitter.all.clear()
mitt源码分析
// TS定义的事件类型
export type EventType = string | symbol;
export type Handler<T = unknown> = (event: T) => void;
export type WildcardHandler<T = Record<string, unknown>> = (
type: keyof T,
event: T[keyof T]
) => void;
export type EventHandlerList<T = unknown> = Array<Handler<T>>;
export type WildCardEventHandlerList<T = Record<string, unknown>> = Array<
WildcardHandler<T>
>;
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>;
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;
}
/**
* 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 GenericEventHandler =
| Handler<Events[keyof Events]>
| WildcardHandler<Events>;
all = all || new Map();
return {
// 缓存所有事件的Map
all,
// 注册事件, type是事件类型, handler是回调函数
on<Key extends keyof Events>(type: Key, handler: GenericEventHandler) {
const handlers: Array<GenericEventHandler> | undefined = all!.get(type);
if (handlers) {
// 如果事件已经存在,则将回调函数添加到Map中
handlers.push(handler);
} else {
// 如果事件不存在,则创建一个新的数组来存储回调函数
all!.set(type, [handler] as EventHandlerList<Events[keyof Events]>);
}
},
// 通过type和handler来移除事件,如果没有提供handler则删除所有该事件的回调函数
off<Key extends keyof Events>(type: Key, handler?: GenericEventHandler) {
const handlers: Array<GenericEventHandler> | undefined = all!.get(type);
if (handlers) {
if (handler) {
//通过将 indexOf 的结果通过 >>> 0 转换,可以确保即使未找到元素,也不会传递一个负值给 splice 方法,从而避免潜在的错误
// >>> 0的主要作用是将操作数转换为 32 位无符号整数,并确保结果是一个非负整数。
handlers.splice(handlers.indexOf(handler) >>> 0, 1);
} else {
all!.set(type, []);
}
}
},
// 触发事件执行
emit<Key extends keyof Events>(type: Key, evt?: Events[Key]) {
// all!.是非空断言,表示all不会只null 或者 undefined
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!);
});
}
}
};
}
tiny-emitter源码分析
// 声明一个函数
function E () {
}
// 函数的原型上绑定事件相关的方法 on once emit off等
E.prototype = {
on: function (name, callback, ctx) {
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
callback.apply(ctx, arguments);
};
listener._ = callback
// 监听listener
return this.on(name, listener, ctx);
},
emit: function (name) {
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);
}
// 返回this,方便链式调用
return this;
},
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++) {
// 判断如果不是同一个回调函数,则保留下来, 注意这里的_是上面once方法中定义的,过滤once中传入的函数
if (evts[i].fn !== callback && evts[i].fn._ !== callback)
liveEvents.push(evts[i]);
}
}
(liveEvents.length)
? e[name] = liveEvents
: delete e[name];
return this;
}
};
module.exports = E;
module.exports.TinyEmitter = E;
总结
实现相对简单,使用频率超高。有问题欢迎指正~