mitt 详解:轻量级事件总线在 Toast 组件中的应用

31 阅读4分钟

在React中, Toast 是一种轻量级的通知组件,主要用于向用户展示简短的操作反馈或状态提示。它通常具有以下特点和作用:

核心含义

Toast 源自移动端开发(如Android/iOS),在React中被广泛借鉴,本质是 临时消息提示框 ,会在屏幕上短暂显示后自动消失,不会阻塞用户操作。

主要作用

  1. 操作结果反馈

    • 成功提示(如"提交成功")、错误提示(如"网络异常")
    • 加载状态(如"数据加载中...")
  2. 轻量级通知

    • 相比模态框(Modal)更简洁,不需要用户手动关闭
    • 适合展示非关键性信息(如"已保存草稿")
  3. 用户体验优化

    • 提供即时视觉反馈,增强交互感知
    • 避免打断用户当前操作流程

那么什么是 mitt?

mitt 是一个超轻量级的 JavaScript 事件发射器库(gzip 压缩后仅 200 bytes),它实现了 发布-订阅模式 (Pub/Sub),允许你在应用的不同组件之间建立松耦合的通信机制。

简单说,mitt 就像一个"事件邮局":

  • 组件可以"订阅"感兴趣的事件(留下联系方式)
  • 其他组件可以"发布"事件(寄信)
  • 邮局负责将事件传递给所有订阅者

核心 API(极简设计)

import mitt from 'mitt'
const emitter = mitt()

// 订阅事件
emitter.on('eventName', (data) => { /* 处理事件 */ })

// 发布事件
emitter.emit('eventName', { message: 'Hello' })

// 取消订阅
emitter.off('eventName', handler)

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

具体作用

在项目逻辑中,mitt 被用作 全局事件总线 ,专门处理 Toast 组件的显示/隐藏逻辑

1. 实现跨组件通信

Toast 组件通常需要在应用的任何地方被触发(如表单提交成功、网络错误等)。通过 mitt 可以避免组件间的层层传递:

首先在 toastController.js 中创建事件总线

import mitt from 'mitt';
const emitter = mitt(); // 实例化

export default emitter;

然后在 Toast 组件中订阅事件

import emitter from './toastController';

useEffect(() => {
  // 监听 toast 显示事件
  emitter.on('showToast', (options) => {
    setVisible(true);
    setMessage(options.message);
    // ... 其他逻辑
  });

  // 组件卸载时取消订阅
  return () => emitter.off('showToast');
}, []);

最后在任何组件中发布事件(触发 Toast

import emitter from './toastController';

submitForm = () => {
  api.submit().then(() => {
    emitter.emit('showToast', {
      message: '提交成功',
      type: 'success'
    });
  });
};

2. 解耦 Toast 逻辑

没有 mitt 时,你可能需要:

  • 通过 props 层层传递 Toast 控制函数
  • 使用 ContextRedux 管理 Toast 状态
  • 直接操作 DOM(非常不 React

而有了 mitt 后,Toast 组件的逻辑被完全封装,其他组件只需关注事件发布,无需关心 Toast 如何实现。

3. 支持多类型事件扩展

除了显示 Toast,还可以轻松扩展其他事件类型:

// 错误提示
emitter.emit('showError', { message: '网络异常' });

// 加载提示
emitter.emit('showLoading', { text: '加载中...' });

项目中的应用:

比如说现在有这样一个页面:

image.png

我们需要在首页(或者其他地方)放置一个按钮,能够帮我们展示 我的 里面的信息,就需要用到 mitt 来处理 Toast 组件的显示逻辑:

// 让组件基于事件机制来通信
import mitt from 'mitt'; // 自定义事件
// 实例化
export const toastEvents =mitt();

export function showToast(user = 0, bell = 0, mail = 0) {
  // 任何想要与Toast 通信的地方调用
  // emit 发布事件  发布者
  toastEvents.emit('show',{user,bell,mail});
}

Toast组件:

import { useState,useEffect } from 'react'
import { toastEvents } from './toastController';

const Toast = (props)=>{
  const [visible,setIsVisible] = useState(false);
  const [data,setData] = useState({
    user: 0,
    bell: 0,
    mail: 0
  });
  useEffect(()=>{
    const show = (info)=>{
      setData(info);
      setIsVisible(true);
      setTimeout(()=>{
        setIsVisible(false);
      },2000);
    }
    // toastEvents 是 mitt 的实例
    // 自定义事件
    // on 监听一个事件
    // 订阅了show 的事件,订阅者
    toastEvents.on('show',show);
    // 组件销毁时,取消订阅
    return ()=>{
      toastEvents.off('show',show);
    }
  },[])
  
  if(!visible) return null;
  
  return (
    <div className={styles.toastWrapper}>
      <div className={styles.toastItem}>👤 {data.user}</div>
      <div className={styles.toastItem}>🔔 {data.bell}</div>
      <div className={styles.toastItem}>✉️ {data.mail}</div>
      <div className={styles.toastArrow}></div>
    </div>
  )
}
export default Toast;

然后在首页进行调用

const Home = () => {
  useTitle('Go Where Well');
  return (
    <div className={styles.container}>
      <Button type="primary" onClick={()=>{
        {* 调用 Toast *}
        showToast(1,2,3);
      }}>
        显示Toast
      </Button>
    </div>
  )
}

最后效果如图所示

image.png

总结

mitt 在项目中扮演了"通信桥梁"的角色,它让 Toast 组件从全局状态管理中解放出来,实现了真正的组件解耦。这种模式特别适合:

  • 全局提示组件(Toast/Modal/Notification)
  • 跨组件状态同步
  • 插件化架构设计

下次当你需要在 React 项目中实现简单高效的组件通信时,不妨试试 mitt——小而美的解决方案往往最能解决实际问题!