Mitt
js 的 EventEmmiter(类比 Node 的 EventEmmiter),100 行左右的代码,可用于多种环境,Vue3.x 推荐使用,比起 Vue 自带的 EventBus,优点是不依赖 vue 实例,类似的对比浏览器的自定义事件,优点是不依赖 DOM 实例。当然这个库的缺点也是太轻量了,并且稍微复杂点的项目,会显得通信很混乱。需要更丰富的功能可以使用 emittery 或者自行开发一套 ~
功能实现
主体功能的实现可以参考下面的注释:
export default function mitt(all) {
// Map 维护事件列表,key + handlers
all = all || new Map();
return {
all,
// 监听事件
on(type, handler) {
const handlers = all.get(type);
if (handlers) {
handlers.push(handler);
} else {
all.set(type, [handler]);
}
},
// 移除事件
off(type, handler) {
const handlers = all.get(type);
if (handlers) {
if (handler) {
// 确保 splice 参数是非负数,负数的话会尝试从末尾位置开始处理,比如-1是倒数第一个元素
handlers.splice(handlers.indexOf(handler) >>> 0, 1);
} else {
all.set(type, []);
}
}
},
// 触发事件
emit(type, evt) {
let handlers = all.get(type);
// 遍历触发同类事件
if (handlers) {
// NOTE 在事件的触发过程中,可能会有订阅者动态地取消订阅事件
// 通过 slice 浅拷贝数组,可以避免影响到原始的事件处理函数数组
handlers.slice().map((handler) => {
handler(evt);
});
}
handlers = all.get("*");
// 触发通用事件
if (handlers) {
handlers.slice().map((handler) => {
handler(type, evt);
});
}
},
};
}
默认导出的函数支持传参,这个参数是自行实现的事件监听对象,可以理解为是对默认方法的扩展。比如我们可以使用 mitt() 函数和自定义的 emitter 来创建一个使用 WebSocket 通信的通用方法
import mitt from "mitt";
class MyEmitter {
constructor(socket) {
this.socket = socket;
}
on(type, handler) {
this.socket.on(type, handler);
}
off(type, handler) {
this.socket.off(type, handler);
}
emit(type, data) {
this.socket.send(JSON.stringify({ type, data }));
}
}
// 创建 WebSocket 连接
const socket = new WebSocket("wss://example.com");
// 使用自定义的 emitter 实现 WebSocket 客户端
const emitter = mitt(new MyEmitter(socket));
// 订阅事件
emitter.on("message", (event) => {
console.log(`Received message: ${event.data}`);
});
// 触发事件
emitter.emit("send", { text: "Hello, server!" });
功能还是相对简单,缺少 once、前置、后置事件等等
ts 定义
ts 定义很完善,也确实有一些技巧在里面,可以研究研究 ~
打包配置
基于 microbundle
,支持打包生成多种模块(es/cjs/umd)
这个库是零配置的,不像 rollup、webpack 都还至少需要一份额外的配置文件,只需要在 package.json
里配置规则就行。但是这个库生态会比较弱,插件不及其他打包工具,从这里也能看出它只能用于小型项目。另外就是这个库也支持 tree-shaking
单元测试
单元测试也是很齐全,基于 mocha
,使用文档参考 https://mochajs.org/#getting-started