源码学习目录

1. 前言

1.1 环境

  1. 操作系统: macOS 11.5.2
  2. 浏览器: Chrome 94.0.4606.81
  3. mitt 3.0.0
  4. tiny-emitter 2.1.0

    1.2 阅读该文章可以get以下知识点

  • 了解 mitt、tiny-emitter 作用和使用场景
  • 学习 发布订阅 设计模式

    2. 开始

    2.1 mitt 使用方法

    ```javascript import mitt from ‘mitt’

const emitter = mitt()

// listen to an event emitter.on(‘foo’, e => console.log(‘foo’, e) )

// listen to all events emitter.on(‘*’, (type, e) => console.log(type, e) )

// fire an event emitter.emit(‘foo’, { a: ‘b’ })

// clearing all events emitter.all.clear()

// working with handler references: function onFoo() {} emitter.on(‘foo’, onFoo) // listen emitter.off(‘foo’, onFoo) // unlisten

  1. <a name="ow0sa"></a>
  2. ## 2.2 mitt 源码
  3. ```javascript
  4. /** ts声明,非逻辑代码 start **/
  5. // 事件类型是字符串或者symbol
  6. export type EventType = string | symbol;
  7. // An event handler can take an optional event argument
  8. // and should not return a value
  9. // 传入事件
  10. export type Handler<T = unknown> = (event: T) => void;
  11. // 通配符处理,传入类型和事件
  12. export type WildcardHandler<T = Record<string, unknown>> = (
  13. type: keyof T,
  14. event: T[keyof T]
  15. ) => void;
  16. // An array of all currently registered event handlers for a type
  17. // 事件回调数组
  18. export type EventHandlerList<T = unknown> = Array<Handler<T>>;
  19. // 通配符事件回调数组
  20. export type WildCardEventHandlerList<T = Record<string, unknown>> = Array<WildcardHandler<T>>;
  21. // A map of event types and their corresponding event handlers.
  22. // 事件类型和事件回调列表
  23. export type EventHandlerMap<Events extends Record<EventType, unknown>> = Map<
  24. keyof Events | '*',
  25. EventHandlerList<Events[keyof Events]> | WildCardEventHandlerList<Events>
  26. >;
  27. export interface Emitter<Events extends Record<EventType, unknown>> {
  28. all: EventHandlerMap<Events>;
  29. // ts的重载,事件注册,可以传type也可以传*,进入到不同的handler回调
  30. on<Key extends keyof Events>(type: Key, handler: Handler<Events[Key]>): void;
  31. on(type: '*', handler: WildcardHandler<Events>): void;
  32. // 事件解绑
  33. off<Key extends keyof Events>(type: Key, handler?: Handler<Events[Key]>): void;
  34. off(type: '*', handler: WildcardHandler<Events>): void;
  35. // 事件发布
  36. emit<Key extends keyof Events>(type: Key, event: Events[Key]): void;
  37. emit<Key extends keyof Events>(type: undefined extends Events[Key] ? Key : never): void;
  38. }
  39. /** ts声明,非逻辑代码 end **/
  40. /**
  41. * Mitt: Tiny (~200b) functional event emitter / pubsub.
  42. * @name mitt
  43. * @returns {Mitt}
  44. */
  45. export default function mitt<Events extends Record<EventType, unknown>>(
  46. all?: EventHandlerMap<Events>
  47. ): Emitter<Events> {
  48. // type类型
  49. type GenericEventHandler =
  50. | Handler<Events[keyof Events]>
  51. | WildcardHandler<Events>;
  52. // all可以传入map或者创建一个map
  53. all = all || new Map();
  54. return {
  55. // type名称合集
  56. /**
  57. * A Map of event names to registered handler functions.
  58. */
  59. all,
  60. /**
  61. * Register an event handler for the given type.
  62. * @param {string|symbol} type Type of event to listen for, or `'*'` for all events
  63. * @param {Function} handler Function to call in response to given event
  64. * @memberOf mitt
  65. */
  66. // 事件注册, 事件名和回调函数
  67. on<Key extends keyof Events>(type: Key, handler: GenericEventHandler) {
  68. // 事件名的列表是否存在,存在直接从all中get
  69. const handlers: Array<GenericEventHandler> | undefined = all!.get(type);
  70. // 存在该事件名列表,直接push
  71. if (handlers) {
  72. handlers.push(handler);
  73. }
  74. // 不存在,设置一个新的 {type: [handler]}
  75. else {
  76. all!.set(type, [handler] as EventHandlerList<Events[keyof Events]>);
  77. }
  78. },
  79. /**
  80. * Remove an event handler for the given type.
  81. * If `handler` is omitted, all handlers of the given type are removed.
  82. * @param {string|symbol} type Type of event to unregister `handler` from, or `'*'`
  83. * @param {Function} [handler] Handler function to remove
  84. * @memberOf mitt
  85. */
  86. // 事件解绑,事件名,回调函数选传
  87. off<Key extends keyof Events>(type: Key, handler?: GenericEventHandler) {
  88. // 查看是否存在该事件列表
  89. const handlers: Array<GenericEventHandler> | undefined = all!.get(type);
  90. if (handlers) {
  91. // 如果传入了回调函数,只从回调列表中删除这一个函数,其他的保留
  92. if (handler) {
  93. // >>> 这个操作符就像>>操作符一样,只是左移的位总是零。
  94. // >>> 不太会用,只知道大于等于0都是原来的值,如果小于0,是一个非常大的数字,基本越界,超出了handlers的范围
  95. handlers.splice(handlers.indexOf(handler) >>> 0, 1);
  96. }
  97. else {
  98. // 清空该回调列表
  99. all!.set(type, []);
  100. }
  101. }
  102. },
  103. /**
  104. * Invoke all handlers for the given type.
  105. * If present, `'*'` handlers are invoked after type-matched handlers.
  106. *
  107. * Note: Manually firing '*' handlers is not supported.
  108. *
  109. * @param {string|symbol} type The event type to invoke
  110. * @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler
  111. * @memberOf mitt
  112. */
  113. // 事件发布
  114. emit<Key extends keyof Events>(type: Key, evt?: Events[Key]) {
  115. // 获取事件回调列表
  116. let handlers = all!.get(type);
  117. if (handlers) {
  118. (handlers as EventHandlerList<Events[keyof Events]>)
  119. // 创建一个新的列表
  120. .slice()
  121. .map((handler) => {
  122. handler(evt!);
  123. });
  124. }
  125. // 获取属于* 的回调列表
  126. handlers = all!.get('*');
  127. if (handlers) {
  128. (handlers as WildCardEventHandlerList<Events>)
  129. .slice()
  130. .map((handler) => {
  131. // 传入事件类型和值
  132. handler(type, evt!);
  133. });
  134. }
  135. }
  136. };
  137. }

2.3 tiny-emitter 使用

  1. var Emitter = require('tiny-emitter');
  2. var emitter = new Emitter();
  3. emitter.on('some-event', function (arg1, arg2, arg3) {
  4. //
  5. });
  6. emitter.emit('some-event', 'arg1 value', 'arg2 value', 'arg3 value');

2.4 tiny-emitter 源码

  1. // 这个写法不太推荐,声明了一个无语义化的构造函数
  2. function E () {
  3. // Keep this empty so it's easier to inherit from
  4. // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)
  5. }
  6. E.prototype = {
  7. // 事件注册 名称,回调,上下文
  8. on: function (name, callback, ctx) {
  9. // 这个e相当于mitt的map对象,存储事件和回到函数
  10. var e = this.e || (this.e = {});
  11. // 简写,当列表存在和不存在直接赋值[]
  12. (e[name] || (e[name] = [])).push({
  13. fn: callback,
  14. ctx: ctx
  15. });
  16. return this;
  17. },
  18. // 事件注册,只执行依次
  19. once: function (name, callback, ctx) {
  20. var self = this;
  21. // 回调函数
  22. function listener () {
  23. // 执行回调时,解绑这一个回调,其他相同的事件名称还是可以监听到发布的消息
  24. self.off(name, listener);
  25. // 执行回调函数
  26. callback.apply(ctx, arguments);
  27. };
  28. // 在listener上在绑定了回调函数,可能有些处理用吧,怪异的写法
  29. listener._ = callback
  30. return this.on(name, listener, ctx);
  31. },
  32. // 事件发布
  33. emit: function (name) {
  34. // 支持数据多参数,将name这个参数去除了,这里是和mitt的差异点
  35. var data = [].slice.call(arguments, 1);
  36. // 生成新的数组,不修改原数组
  37. var evtArr = ((this.e || (this.e = {}))[name] || []).slice();
  38. var i = 0;
  39. var len = evtArr.length;
  40. // 循环执行所有的回调事件
  41. for (i; i < len; i++) {
  42. evtArr[i].fn.apply(evtArr[i].ctx, data);
  43. }
  44. return this;
  45. },
  46. // 事件解绑,这里的回调函数必传,不支持批量删除所有的事件,这里是和mitt的差异点
  47. off: function (name, callback) {
  48. var e = this.e || (this.e = {});
  49. var evts = e[name];
  50. var liveEvents = [];
  51. // 回调函数需要传的情况,删除这个函数
  52. if (evts && callback) {
  53. for (var i = 0, len = evts.length; i < len; i++) {
  54. if (evts[i].fn !== callback && evts[i].fn._ !== callback)
  55. liveEvents.push(evts[i]);
  56. }
  57. }
  58. // Remove event from queue to prevent memory leak
  59. // Suggested by https://github.com/lazd
  60. // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910
  61. // 如果所有的回调事件都解绑了,直接删除事件列表
  62. (liveEvents.length)
  63. ? e[name] = liveEvents
  64. : delete e[name];
  65. return this;
  66. }
  67. };
  68. module.exports = E;
  69. module.exports.TinyEmitter = E;

3. 总结

  1. mitt使用documentation自动生成文档,基于注释注释
  2. 这个语法可以用在indexOf配合splice处理,小于0基本会越界,不会删除数组中的值,如-1 >>>0 等于4294967295,依次往上加1

  3. mitt使用microbundle打包cjs,umd,mjs
  4. tiny-emitter 使用 browserify,没有microbundle的好用,需要配置的东西比较多
  5. tiny-emitter 和 mitt在发布订阅机制上逻辑是相同的,区别 | | mitt | tiny-emitter | | —- | —- | —- | | emit支持传递2个以上参数 | 不支持 | 支持 | | off是否支持全量解绑 | 支持 | 不支持 | | once只执行一次 | 不支持 | 支持 | | 是否支持监听所有事件* | 支持 | 不支持 | | 是否可以从外部传入事件map列表 | 支持 | 不支持 |

参考文档