vue3事件库mitt原理解读及实现

377 阅读3分钟

vue3事件库mitt原理解读及实现

因为vue3去除了on和off方法,这样也导致在vue2中使用的事件总线不能在vue3上继续使用,以下是对于mitt库的解读和重新实现。

mitt的基本思想是构造出一个对象,该对象满足下面的四个基本要求:

  1. 有存储各种事件回调函数容器
  2. 可以对相应的的事件绑定回调函数
  3. 可以触发相关的回调函数
  4. 可以取消相关事件的回调函数

上面的四个点对应着这个对象有四个属性方法,分别是:all,on,offemit

type EventType = string | symbole //事件的名称是字符串或者符号类型
type Handler<T> = (event: T) => void //接收一个参数T的消费型函数

//存储事件回调函数的容器,那么就有两个结构来存储,这里源码用的是array,而这里选择使用set
//map中装入Array来存储
//type EventHandlerMap<Events extends Record<EventType, unknow>> = Map<keyof Events,Array<Handler<Events[keyof Events]>>>

//map中装入set来存储
type EventHandlerMap<Events extends Record<EventType,unknow>> = Map<keyof Events,Set<Handler<Events[keyof Events]>>>
interface Emitter<Events extends Record<EventType,unknow>>{
    all: EventHanlderMap<Events>
    on<Key extends keyof Events>(type: Key, handler: Handler<Events[Key]>): void
    off<Key extends keyof Events>(type: Key): void
    emit<Key extends keyof Events>(type:Key, args: Events[Key]): void
}

这里的所有回调函数都只接收一个参数。例如:

type MyMitter = {
    oneEvent: string
    hello: {
        name: string
        age: number
    }
}
const emitter:Emitter<MyMitter> ;
//这里的emitter就是一个事件对象,它有oneEvent和hello两个事件,
//oneEvent事件的回调函数接受一个字符串函数即 (arg: string) => void
//hello事件的回调函数接收一个{name: string, age: number}类型参数的函数即
//   (arg: {name: string, age: number}) => void

那么目标就清晰了 => 构造出这个对象

那么构造这个对象又有多种方法了,官方使用是一个函数接收一个参数,返回这个需要构造的对象。源码如下:

export default function mitt<Events extends Record<EventType, unknown>>(
	all?: EventHandlerMap<Events>
): Emitter<Events> {
	//type GenericEventHandler =
	//	| Handler<Events[keyof Events]>
	//	| WildcardHandler<Events>;源码后面那种在这里可以忽略
    type GenericEventHandler = Handler<Events[keyof 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!);
					});
			}
		}
	};
}

而这里使用类的方式进行构造

//使用Map<EventType,Set<Handler>>结构
type EventHandlersMap<Events extends Record<EventType, unknown>> = Map<
  keyof Events,
  Set<Handler<Events[keyof Events]>>
>;

class Mitter<Events extends Record<EventType, unknown>> {
  constructor(private all?: EventHandlersMap<Events>) {
    this.all = all || new Map();
  }
  on<Key extends keyof Events>(type: Key, handler: Handler<Events[Key]>): void {
    type GenericEventHandler = Handler<Events[keyof Events]>
    let handlers = this.all!.get(type);
    if (!handlers) {
      this.all!.set(type, (handlers = new Set()));
    }
    handlers.add(handler as GenericEventHandler);
  }
  emit<Key extends keyof Events>(type: Key, arg: Events[Key]): void {
    const handlers = this.all!.get(type);
    if (!handlers || handlers.size === 0) return;
    else {
      for (let handler of handlers) {
        handler.call(null, arg);
      }
    }
  }
  off<Key extends keyof Events>(type: Key): void {
    this.all!.delete(type);
  }
  clear(): void {
    this.all = new Map();
  }
}

这里也就是顺便加了个清空所有回调函数的的方法。

到这里也就结束啦!若有错误还请指正!