介紹
Tiny 200b functional event emitter / pubsub.
200b大小的微型功能事件釋出/訂閱庫
Mitt was made for the browser, but works in any JavaScript runtime. It has no dependencies and supports IE9+.
Mitt是為浏覽器設計的,但可以在任意JavaScript運作時使用,無依賴且支援IE9+。
一般在現代架構跨元件通信時會比較常用,例如在vue跨元件通信時會比較有用,在vue2中可以使用自帶的 $on,$off,$emit 等api進行通信,當在vue3中移除了這些api,取而代之的有 mitt 或者 tiny-emitter 。
install
npm install --save mitt
- ES6 Modules
import mitt from "mitt"
- CommonJs Modules
const mitt = require("mitt");
- CDN
<script src="https://unpkg.com/mitt/dist/mitt.umd.js"></script>
// 然後在wndow上通路
window.mitt
Usage
- 基本使用
import mitt from 'mitt';
const emitter = mitt();
// 監聽事件
emitter.on('foo', e => console.log('foo', e) );
// 解除監聽
emitter.off('foo', onFoo);
// 監聽全部事件
emitter.on('*', (type, e) => console.log(type, e) );
// 觸發事件
emitter.emit('foo', { a: 'b' });
// 清除所有事件監聽
emitter.all.clear()
- typescript支援
import mitt from 'mitt';
type Events = {
foo: string;
bar?: number;
};
const emitter = mitt<Events>();
emitter.on('foo', (e) => {}); // e的類型為string
- 簡單的封裝
// mitt.ts
import mitt from "mitt";
type Events = {
change: string | undefined;
submit: number[]
}
const emitter = mitt<Events>();
export default emitter;
// 元件 A
<script setup lang="ts">
import Emitter from "@/shared/mitt";
Emitter.emit("submit", [1]);
</script>
// 元件B
<script setup lang="ts">
import Emitter from "@/shared/mitt";
Emitter.on("submit", (params) => {
// params 的類型 => number[]
console.log(params);
})
</script>
源碼解析
完整的源碼如下
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>;
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 {
/**
* 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) {
const handlers: Array<GenericEventHandler> | undefined = all!.get(type);
if (handlers) {
handlers.push(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 (`'*'` to remove a wildcard handler)
* @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) {
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!);
});
}
}
};
}
可以看到,整個庫暴露的api隻有以下4個
all,on(),off(),emit()
接下來了解一下每個api的實作
- all
從源碼可以看出 all 是一個 Map 對象,用來存儲所有的事件,如果初始化沒有傳入會自動建立一個空的 Map 對象,是以支援所有 Map 的方法,如:
// 擷取監聽函數
Emitter.all.get(key);
// 設定監聽事件
Emitter.all.set(key, [handler]);
// 清除所有事件
Emitter.all.clear();
- on
on<Key extends keyof Events>(type: Key, handler: GenericEventHandler) {
const handlers: Array<GenericEventHandler> | undefined = all!.get(type);
if (handlers) {
handlers.push(handler);
}
else {
all!.set(type, [handler] as EventHandlerList<Events[keyof Events]>);
}
},
on() 用于監聽事件,接收兩個參數:
- type 事件名稱
- handler 回調函數
Emitter.on(type, handler);
在函數内部,首先擷取對應 type 的 handler 清單,如果存在,則向 handlers 中 push
const handlers: Array<GenericEventHandler> | undefined = all!.get(type);
if (handlers) {
handlers.push(handler);
}
如果不存在,則設定對應事件處理回調
all!.set(type, [handler] as EventHandlerList<Events[keyof Events]>);
- off
off<Key extends keyof Events>(type: Key, handler?: GenericEventHandler) {
const handlers: Array<GenericEventHandler> | undefined = all!.get(type);
if (handlers) {
if (handler) {
handlers.splice(handlers.indexOf(handler) >>> 0, 1);
}
else {
all!.set(type, []);
}
}
}
off() 用于移除事件監聽,接收兩個參數:
- type 事件名稱
- handler 要移除的回調函數,可選
首先擷取對應 type 的 handler 清單,然後判斷 handlers 是否存在,如果此時傳入了 handler,則删除對應的 handler
if (handler) {
handlers.splice(handlers.indexOf(handler) >>> 0, 1);
}
需要注意的是,這裡使用了 無符号右移運算符(>>>)(具體用法請查閱MDN),且移位了 0,對于 非負數 來說,進行該運算沒有任何作用,而如果是 -1 >>> 0,其運算結果就是4294967295,而 splice 方法如果傳入的 start > array.length,并不會删除任何元素,其實就是省略了一步判斷而已,使代碼更精簡,相當于如下代碼
let index = handlers.indexOf(handler);
if (index > -1) {
handlers.splice(index, 1);
}
如果沒有傳入 handler,則清空對應的監聽清單
all!.set(type, []);
- emit
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!);
});
}
}
emit() 用于觸發事件監聽,接收兩個參數:
- type 事件名稱
- evt 傳入回調的參數
emit() 的邏輯就是擷取對應的 handlers,然後循環執行回調,并且會通知 type = * 的訂閱回調
以上就是本文的全部内容了,如果覺得有用的話,可以關注作者哦!
本文為原創作品,未經同意禁止轉載!