龙空技术网

【源码阅读】一个只有200b的javascript发布订阅库-mitt

橡树果前端工作室 869

前言:

此刻朋友们对“html工作室源码”都比较讲究,兄弟们都需要分析一些“html工作室源码”的相关资讯。那么小编也在网上网罗了一些有关“html工作室源码””的相关知识,希望姐妹们能喜欢,兄弟们快快来学习一下吧!

介绍

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=";></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.tsimport 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 valueexport 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 typeexport 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 = * 的订阅回调

以上就是本文的全部内容了,如果觉得有用的话,可以关注作者哦!

本文为原创作品,未经同意禁止转载!

标签: #html工作室源码