mitt、tiny-emitter发布订阅学习

121 阅读3分钟

前言

本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。

**这是源码共读的第8期,链接:**juejin.cn/post/708498…

发布订阅模式简介

发布订阅是一种设计模式,包含发布者、订阅者和中间平台三者

  • 发布者:负责发送消息到特定的主题,不关心谁接收消息
  • 订阅者:订阅一个或多个主题,只接收感兴趣的的消息
  • 中间平台:中间组件,负责管理主题的订阅关系、消息的分发 例如Vue的EventBus就使用了发布订阅,通过 $emit(发布)和 $on(订阅)传递消息
// eventBus就是一个收集事件的中间平台
const eventBus = new Vue()
// 发布者
eventBus.$emit('message', 'hi message')
// 订阅者
eventBus.$on('message', (data) => {
    console.log(data)
})

mitt简单使用

import mitt from 'mitt'

const emitter = mitt()

// 监听订阅一个事件
emitter.on('go', e => console.log('go go go', e) )

// 触发一个事件
emitter.emit('go', 'hi, im a message')

// 清空所有的事件
emitter.all.clear()

mitt源码分析

// TS定义的事件类型
export type EventType = string | symbol;

export type Handler<T = unknown> = (event: T) => void;
export type WildcardHandler<T = Record<string, unknown>> = (
	type: keyof T,
	event: T[keyof T]
) => void;

export type EventHandlerList<T = unknown> = Array<Handler<T>>;
export type WildCardEventHandlerList<T = Record<string, unknown>> = Array<
	WildcardHandler<T>
>;

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 {
		// 缓存所有事件的Map
		all,

		// 注册事件, type是事件类型, handler是回调函数
		on<Key extends keyof Events>(type: Key, handler: GenericEventHandler) {
			const handlers: Array<GenericEventHandler> | undefined = all!.get(type);
			if (handlers) {
				// 如果事件已经存在,则将回调函数添加到Map中
				handlers.push(handler);
			} else {
				// 如果事件不存在,则创建一个新的数组来存储回调函数
				all!.set(type, [handler] as EventHandlerList<Events[keyof Events]>);
			}
		},

		// 通过type和handler来移除事件,如果没有提供handler则删除所有该事件的回调函数
		off<Key extends keyof Events>(type: Key, handler?: GenericEventHandler) {
			const handlers: Array<GenericEventHandler> | undefined = all!.get(type);
			if (handlers) {
				if (handler) {
					//通过将 indexOf 的结果通过 >>> 0 转换,可以确保即使未找到元素,也不会传递一个负值给 splice 方法,从而避免潜在的错误
					// >>> 0的主要作用是将操作数转换为 32 位无符号整数,并确保结果是一个非负整数。
					handlers.splice(handlers.indexOf(handler) >>> 0, 1);
				} else {
					all!.set(type, []);
				}
			}
		},

		// 触发事件执行
		emit<Key extends keyof Events>(type: Key, evt?: Events[Key]) {
			// all!.是非空断言,表示all不会只null 或者 undefined
			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!);
					});
			}
		}
	};
}

tiny-emitter源码分析

// 声明一个函数
function E () {
}

// 函数的原型上绑定事件相关的方法 on once emit off等
E.prototype = {
  on: function (name, callback, ctx) {
    var e = this.e || (this.e = {});

	// 如果事件不存在,则创建一个数组来存储回调函数
    (e[name] || (e[name] = [])).push({
      fn: callback,
      ctx: ctx
    });

    return this;
  },

  once: function (name, callback, ctx) {
    var self = this;
    function listener () {
	  // 移除事件监听器
      self.off(name, listener);
	  // 执行回调函数,运用了闭包缓存了callback
      callback.apply(ctx, arguments);
    };

    listener._ = callback
	// 监听listener
    return this.on(name, listener, ctx);
  },

  emit: function (name) {
    var data = [].slice.call(arguments, 1);
    var evtArr = ((this.e || (this.e = {}))[name] || []).slice();
    var i = 0;
    var len = evtArr.length;

	// 循环执行回调函数
    for (i; i < len; i++) {
      evtArr[i].fn.apply(evtArr[i].ctx, data);
    }
	// 返回this,方便链式调用
    return this;
  },

  off: function (name, callback) {
    var e = this.e || (this.e = {});
    var evts = e[name];
    var liveEvents = [];

    if (evts && callback) {
      for (var i = 0, len = evts.length; i < len; i++) {
		// 判断如果不是同一个回调函数,则保留下来, 注意这里的_是上面once方法中定义的,过滤once中传入的函数
        if (evts[i].fn !== callback && evts[i].fn._ !== callback)
          liveEvents.push(evts[i]);
      }
    }
	
    (liveEvents.length)
      ? e[name] = liveEvents
      : delete e[name];

    return this;
  }
};

module.exports = E;
module.exports.TinyEmitter = E;

总结

实现相对简单,使用频率超高。有问题欢迎指正~