- mitt API方法有:
- on 订阅事件;
- emit 发布事件;
- off 取消订阅的事件。
本质上就是一个Map对象,它用于存储订阅事件。
看代码前想象Map的数据结构:key—value
import mitt from 'mitt';
// 创建一个事件管理器
const emitter = mitt();
// 注册一个事件
emitter.on('click', (evt) => {
console.log('clicked', evt.target);
});
// 再注册一个事件
emitter.on('click', (evt) => {
console.log('clicked', evt.target2);
});
// 触发一个事件
emitter.emit('click', { target: '我是粽叶', target2: '我是粽叶二号' });
// 取消一个事件
emitter.off('click');
运行一下:
2.去除掉TypeScript类型,将其修改成javaScript实现(对照使用例子来理解各个方法):
export default function mitt(all){
//使用Map存储注册的事件
all = all || new Map();
return {
all,
//on(type, handler) {...}:注册一个事件,
//接受两个参数:type表示事件类型,handler表示事件处理函数。
//如果已经存在该类型的事件,则将该处理函数添加到已有的处理函数列表中,否则创建一个新的处理函数列表。
on(type, handler) {
const handlers= all!.get(type);
if (handlers) {
handlers.push(handler);
}
else {
all!.set(type, [handler])
}
},
//off(type, handler) {...}:取消注册一个事件,
//接受两个参数:type表示事件类型,handler表示事件处理函数。
//如果存在该类型的事件,则从已有的处理函数列表中删除该处理函数,
//如果没有传入handler参数,则删除该类型的所有处理函数。
off(type, handler) {
const handlers = all!.get(type);
if (handlers) {
if (handler) {
const index = handlers.indexOf(handler)
handlers.splice(index > -1 ? index : 0, 1);
}
else {
all!.set(type, []);
}
}
},
//emit(type, evt) {...}:触发一个事件
emit(type, evt) {
let handlers = all!.get(type);
if (handlers) {
handlers.slice().map((handler) => {
handler(evt);
});
}
//判断是否存在"*" 订阅的事件,"*"注册的事件进行兜底
handlers = all!.get('*');
if (handlers) {
handlers.slice().map((handler) => {
handler(type, evt);
});
}
}
};
}
在emit方法中为什么要handlers.slice().map()?
下面给出一个例子来证明为什么要写handlers.slice():
假设有一个事件类型myEvent,已经注册了两个处理函数handler1和handler2,并且在执行handler1时会删除handler2。
如果直接使用原列表handlers,则在执行handler2时会出现问题,因为此时handler2已经被删除了。
为了避免这种情况,所以需要先复制一份处理函数列表,即使删除了原数组当中的某个值那和我复制的有啥关系?我继续在复制的列表上依次执行处理函数。具体代码如下:
const handlers = [handler1, handler2];
handlers.slice().map((handler) => {
handler();
});
以大家的水平理解起来just so so~
这样我们就学会了一个拥有9k Star库的所有源码。(完结撒花)
.
.
.
.
.
.
.
.
但是我们能止步于此嘛?我们已经站在了mitt的肩膀上看到了新的风景,那为什么我们不更进一步?
1.观察源码发现这些代码都是同步的,那假如我注册的一个事件是异步(例如一个请求)怎么办?
在emit方法中,将处理函数列表改为异步函数列表,即将所有处理函数改为异步函数,例如:
const asyncHandlers = handlers.map(async (handler) => {
await handler(evt);
});
2.事件优先级:在on方法中,可以新增一个可选参数priority,表示事件处理函数的优先级,优先级越高的处理函数会先执行。在emit方法中,可以按照优先级从高到低依次执行处理函数。
on(type, handler, priority = 0) {
const handlers = all!.get(type) || [];
handlers.push({handler, priority});
//按照优先级从高到低
handlers.sort((a, b) => b.priority - a.priority);
all!.set(type, handlers);
},
3.事件监听器函数:在on方法中,可以新增一个可选参数listener,表示事件监听器,用于监听事件的触发情况。在emit方法中,可以在执行处理函数前先执行监听器,例如记录事件触发次数、事件触发时间等信息。
on(type, handler, priority = 0, listener) {
const handlers = all!.get(type) || [];
handlers.push({handler, priority});
handlers.sort((a, b) => b.priority - a.priority);
all!.set(type, handlers);
if (listener) { //当注册的时候判断是否传入
listener({type, handler, priority});
}
},
emit(type, evt, listener) {
if (listener) {
listener({type, evt});
}
let handlers = all!.get(type);
if (handlers) {
handlers.slice().map((item) => {
item.handler(evt);
});
}
}
具体使用:
function eventListener({type, handler, priority}) {
console.log(`事件 ${type} 已注册,处理函数为 ${handler},优先级为 ${priority}`);
}
function eventTriggerListener({type, evt}) {
console.log(`事件 ${type} 已触发,事件对象为 ${evt}`);
}
const bus = mitt();
bus.on('click', () => console.log('点击事件已触发'), 1, eventListener);
bus.emit('click', {target: '按钮'}, eventTriggerListener);
完整代码:
export default function mitt(all){
//使用Map存储注册的事件
all = all || new Map();
return {
all,
on(type, handler, priority = 0, listener) {
const handlers = all!.get(type) || [];
handlers.push({handler, priority});
handlers.sort((a, b) => b.priority - a.priority);
all!.set(type, handlers);
if (listener) {
listener({type, handler, priority});
}
},
off(type, handler) {
const handlers = all!.get(type);
if (handlers) {
if (handler) {
const index = handlers.findIndex(item => item.handler === handler);
handlers.splice(index > -1 ? index : 0, 1);
}
else {
all!.set(type, []);
}
}
},
emit(type, evt, listener) {
if (listener) {
listener({type, evt});
}
let handlers = all!.get(type);
if (handlers) {
handlers.slice().map(async (handler) => {
await handler(evt);
});
}
}
};
}
function eventListener({type, handler, priority}) {
console.log(`事件 ${type} 已注册,处理函数为 ${handler},优先级为 ${priority}`);
}
function eventTriggerListener({type, evt}) {
console.log(`事件 ${type} 已触发,事件对象为 ${evt}`);
}
const bus = mitt();
bus.on('click', () => console.log('点击事件已触发'), 1, eventListener);
bus.emit('click', {target: '按钮'}, eventTriggerListener);
放在代码中运行:
输出结果: