Skip to content

mitt

[TOC]

索引

  • mitt()(all?),用于创建一个事件总线实例,支持事件的监听、触发和移除。
  • emitter.all{eventName: Handler[]},用于存储所有已注册的事件类型及其对应的事件处理器列表,用于调试或高级操作。
  • emitter.on()(type,handler),用于 监听指定类型的事件,当该事件被触发时,执行绑定的处理函数。支持监听具体事件类型或使用通配符 * 监听所有事件。
  • emitter.off()(type,handler?),用于 移除指定事件类型的监听器,支持移除单个处理函数或清空某事件类型的所有监听器,避免内存泄。
  • emitter.emit()(type,data?),用于 触发指定类型的事件,并传递相关数据给所有监听该事件的处理器。

mitt

Mitt 是一个轻量级的事件发布/订阅(PubSub)库,用于在 JavaScript/TypeScript 中实现组件或模块间的事件通信。

mitt()

mitt()(all?),用于创建一个事件总线实例,支持事件的监听、触发和移除。

  • T默认:Record<string, unknown>,定义事件类型与对应事件数据的类型约束。

  • all?EventHandlerMap<T>默认:{},预定义的事件处理器映射,通常无需手动传入。保留给高级场景或库扩展使用。

  • 返回:

  • emitterEmitter<T>,返回一个 Emitter 对象实例,包含事件监听、触发等方法:

    • emitter.allEventHandlerMap<T>,获取所有已注册的事件处理器,用于调试或高级操作。
    • emitter.on()(type, handler),监听指定类型的事件。
    • emitter.off()(type, handler),移除指定类型的事件监听。
    • emitter.emit()(type, event?),触发指定类型的事件,传递事件数据。
  • 语法:

    • 泛型参数T:用户自定义的事件类型对象。

      ts
      type MyEvents = {
        click: MouseEvent;
        message: string;
        'user-updated': { id: number; name: string };
      };
      const emitter = mitt<MyEvents>();

all

emitter.all{eventName: Handler[]},用于存储所有已注册的事件类型及其对应的事件处理器列表,用于调试或高级操作。

  • 返回:

  • eventNamestring,事件类型。

  • Handler(event)=>void,该事件类型对应的处理器函数数组。

  • 语法:

    • 访问方式

      ts
      const emitter = mitt<MyEvents>();
      const handlersMap = emitter.all; // 直接访问属性
    • 结构示例

      ts
      // 假设监听了一个 'click' 事件和一个通配符 '*' 事件
      emitter.all = {
        'click': [ (event) => console.log(event) ],
        '*': [ (type, event) => console.log(type, event) ]
      };
  • 常见用途:

    • 调试事件监听器:查看所有已注册的事件类型及其处理器数量。

      js
      console.log(emitter.all); 
      // 输出示例: { click: [ [Function] ], '*': [ [Function] ] }
    • 批量移除监听器:清空所有事件类型的监听器。

      js
      Object.keys(emitter.all).forEach(type => {
        emitter.all[type].forEach(handler => {
          emitter.off(type, handler);
        });
      });
    • 动态管理特定事件:直接操作处理器数组(需谨慎)。

      js
      // 移除某个事件类型的所有监听器
      delete emitter.all['click']; 
      
      // 添加一个监听器(等效于 emitter.on('click', handler))
      emitter.all['click'] = emitter.all['click'] || [];
      emitter.all['click'].push(handler);
    • 克隆事件总线状态

      ts
      function cloneEmitter(original) {
        const cloned = mitt();
        Object.entries(original.all).forEach(([type, handlers]) => {
          cloned.all[type] = [...handlers];
        });
        return cloned;
      }
  • 注意事项:

    • 避免直接修改:直接操作 all,如增删数组元素,可能破坏 Mitt 内部状态,建议优先使用 on()/off() 方法。

    • 通配符事件处理:通配符事件 '*' 的处理器存储在 all['*'] 中,其函数签名为 (type, event) => void

    • TS类型安全:若使用泛型定义事件类型,访问 all 时会自动推断键名和处理器类型。

      ts
      type MyEvents = { message: string };
      const emitter = mitt<MyEvents>();
      emitter.on('message', (data) => {}); // data 类型自动推断为 string
      const handlers = emitter.all['message']; // 类型为 Array<Handler<string>>

on()

emitter.on()(type,handler),用于 监听指定类型的事件,当该事件被触发时,执行绑定的处理函数。支持监听具体事件类型或使用通配符 * 监听所有事件。

  • typestring|*,指定要监听的事件类型。若为 *,则监听所有事件。

  • handler:事件触发时执行的回调函数。根据 type 不同,函数参数不同:

    • (data)=>void:监听具体事件时。
    • (type,data)=>void:监听通配符时。
  • 注意事项:

    • 重复监听:同一事件类型可绑定多个处理函数,触发时按添加顺序执行。

    • 内存泄漏:若组件或对象销毁,需调用 off() 移除相关监听,避免残留引用。

    • 匿名函数问题:使用匿名函数作为处理函数时,无法直接通过 off() 移除,需保存函数引用。

      js
      // 错误示例:无法移除
      emitter.on('event', () => {});
      emitter.off('event', () => {}); // 无效
      
      // 正确做法:保存引用
      const handler = () => {};
      emitter.on('event', handler);
      emitter.off('event', handler);
  • 示例:

    • 基础用法

      js
      import mitt from 'mitt';
      const emitter = mitt();
      
      // 监听具体事件
      emitter.on('login', (user) => {
        console.log('User logged in:', user);
      });
      
      // 监听所有事件(通配符)
      emitter.on('*', (eventType, data) => {
        console.log(`Global listener: ${eventType} ->`, data);
      });
      
      // 触发事件
      emitter.emit('login', { name: 'Alice' });
      // 输出:
      // User logged in: { name: 'Alice' }
      // Global listener: login -> { name: 'Alice' }
    • TS类型安全用法

      ts
      type AppEvents = {
        error: Error;
        notification: { title: string; content: string };
      };
      
      const emitter = mitt<AppEvents>();
      
      // 监听具体事件(自动推断参数类型)
      emitter.on('error', (err) => {
        console.error(err.message); // err 类型为 Error
      });
      
      emitter.on('notification', (notice) => {
        console.log(notice.title, notice.content); // notice 类型为 { title: string; content: string }
      });
      
      // 监听所有事件(通配符)
      emitter.on('*', (type, data) => {
        if (type === 'error') {
          console.log('Error occurred:', data.message); // data 类型为 Error
        } else if (type === 'notification') {
          console.log('New notification:', data.title); // data 类型为 { title: string; content: string }
        }
      });
      
      // 触发事件(类型检查)
      emitter.emit('error', new Error('Auth failed'));
      emitter.emit('notification', { title: 'Update', content: 'New version available' });

off()

emitter.off()(type,handler?),用于 移除指定事件类型的监听器,支持移除单个处理函数或清空某事件类型的所有监听器,避免内存泄。

  • typestring|*,指定要移除监听的事件类型。若为*,表示移除通配符监听器。

  • handler?function,指定要移除的特定处理函数。若省略,则移除该事件类型下的所有监听器。

  • 语法:

    • 移除指定的单个监听器:需传入与 on() 绑定时相同的函数引用。

      js
      const handler1 = (data) => console.log('Handler 1:', data);
      const handler2 = (data) => console.log('Handler 2:', data);
      
      emitter.on('event', handler1);
      emitter.on('event', handler2);
      emitter.off('event', handler1); // 仅移除 handler1
    • 清空所有指定类型的监听器

      js
      emitter.on('event', handler1);
      emitter.on('event', handler2);
      emitter.off('event'); // 移除所有 'event' 监听器
    • 清空所有监听器:可通过遍历 all 属性批量移除监听器(谨慎操作)。

      js
      Object.keys(emitter.all).forEach(type => {
        emitter.off(type); // 移除所有类型的监听器
      });
    • 移除通配符监听器:通配符*监听器必须通过 off('*', handler) 移除,不可省略 handler 参数。

      js
      emitter.on('*', wildcardHandler);
      emitter.off('*', wildcardHandler);  // 正确移除

emit()

emitter.emit()(type,data?),用于 触发指定类型的事件,并传递相关数据给所有监听该事件的处理器。

  • typestring,指定要触发的事件类型。

  • data?any,传递给事件处理器的数据。

  • 语法:

    • 触发事件-无数据

      js
      import mitt from 'mitt';
      const emitter = mitt();
      
      // 监听事件
      emitter.on('refresh', () => {
        console.log('Page refreshed');
      });
      // 触发事件(无数据)
      emitter.emit('refresh'); // 输出: Page refreshed
    • 触发事件-带数据

      js
      import mitt from 'mitt';
      const emitter = mitt();
      
      // 监听事件
      emitter.on('message', (data) => {
        console.log('Received:', data);
      });
      // 触发事件(带数据)
      emitter.emit('message', 'Hello World!'); // 输出: Received: Hello World!
    • 触发通配符事件:当调用 emit() 时,所有监听 * 的处理器也会被触发,接收事件类型和数据。

      js
      emitter.on('*', (type, data) => {
        console.log(`[${type}]`, data);
      });
      emitter.emit('message', 'Hello'); 
      // 输出: [message] Hello
    • 默认值处理:若未传递 event,处理器接收 undefined,需在代码中处理可能的空值。

      js
      emitter.on('event', (data) => {
        console.log(data?.value); // 使用可选链避免错误
      });
      emitter.emit('event'); // data 为 undefined
    • TS事件类型约束

      • 必须与泛型 T 中定义的事件类型严格匹配,否则触发类型错误。
      ts
      type Events = { message: string; error: Error };
      const emitter = mitt<Events>();
      
      emitter.emit('message', 'Hello'); // 正确
      emitter.emit('error', new Error('Fail')); // 正确
      emitter.emit('warning', 'Oops'); // TS 报错:'warning' 不在 Events 中定义
      • 若事件类型对应的数据为 void(无数据),可省略 data 参数。否则必须传递 data 参数。
      ts
      type Events = {
        refresh: void;       // 无数据
        update: { id: number };
      };
      
      const emitter = mitt<Events>();
      emitter.emit('refresh');         // 正确(无数据)
      emitter.emit('update', { id: 1 }); // 正确
      emitter.emit('update');          // TS 报错:缺少数据
  • 注意事项:

    • 事件数据不可变性:若传递对象或数组,确保数据未被意外修改,避免副作用。

      js
      const data = { value: 1 };
      emitter.emit('update', data);
      data.value = 2; // 所有监听器看到的 data.value 会被修改!
    • 异步触发问题:emit() 是同步方法,处理器按注册顺序立即执行。若需异步触发,可包裹在 setTimeout 或 Promise 中。

      js
      setTimeout(() => {
        emitter.emit('async-event', 'Delayed data');
      }, 1000);
    • 避免循环触发:防止在处理器中再次触发相同事件,导致无限循环。

      js
      emitter.on('event', () => {
        emitter.emit('event'); // 危险:可能导致堆栈溢出
      });