likes
comments
collection
share

跨组件通信有哪些方式?其实我想说的是事件总线

作者站长头像
站长
· 阅读数 20

谈到组件通信

大家的第一反应都是 父子组件通信 
那么 跨组件的通信方式呢

说到跨组件通信

相信大家很第一反应 都能想到
  • Context API: React 的 Context API 允许你在组件树中传递数据而不必通过 props 手动传递。适用于需要在多个组件中共享数据的场景。

    // 创建 Context
    const MyContext = React.createContext();
    
    // 提供 Context
    function App() {
      return (
        <MyContext.Provider value="Hello from context">
          <ComponentA />
          <ComponentB />
        </MyContext.Provider>
      );
    }
    
    // 使用 Context
    function ComponentA() {
      return (
        <MyContext.Consumer>
          {value => <p>{value}</p>}
        </MyContext.Consumer>
      );
    }
    
    function ComponentB() {
      const value = React.useContext(MyContext);
      return <p>{value}</p>;
    }
    
  • 状态管理库: 使用像 Redux、MobX 或 Zustand 这样的状态管理库来管理和共享全局状态,适用于大型应用或需要复杂状态管理的场景。

    // 使用 Redux
    // actions.js
    export const setMessage = (message) => ({
      type: 'SET_MESSAGE',
      payload: message
    });
    
    // reducer.js
    const initialState = { message: "" };
    export function messageReducer(state = initialState, action) {
      switch (action.type) {
        case 'SET_MESSAGE':
          return { ...state, message: action.payload };
        default:
          return state;
      }
    }
    
    // App.js
    import { useSelector, useDispatch } from 'react-redux';
    import { setMessage } from './actions';
    
    function ComponentA() {
      const dispatch = useDispatch();
      return <button onClick={() => dispatch(setMessage("Hello from Redux"))}>Send Message</button>;
    }
    
    function ComponentB() {
      const message = useSelector(state => state.message);
      return <p>{message}</p>;
    }
    

事件总线

  • 自定义事件总线: 对于不易直接通过 props 或状态管理解决的场景,可以使用自定义事件总线(Event Bus),如使用 Node.js 的 EventEmitter 或类似库来实现。

其实 在某些场景中 事件总线真的很有用 那么 这个模式一般有什么场景使用呢?

事件总线(Event Bus)是一种用于组件间通信的模式,适用于需要解耦和广播事件的场景。它可以在不同组件或模块之间传递事件,而不需要直接依赖于彼此。以下是一些常见的事件总线使用场景:

1. 解耦组件

  • 无关组件间的通信:当两个组件没有直接父子关系时,可以使用事件总线来实现它们之间的通信,而不必通过 props 或 Context 传递数据。
  • 插件系统:在插件系统中,插件可以通过事件总线与主应用进行通信,而不需要直接耦合。

2. 全局事件管理 这个真的很有用

  • 全局状态通知:对于全局性事件(如用户登录、主题更改等),可以通过事件总线来广播通知,所有订阅了这些事件的组件都可以接收到通知并进行相应的处理。
  • 应用级通知:例如,在应用的不同部分(如侧边栏、头部、底部)需要同步状态或处理相同的事件时,事件总线可以作为一个集中管理的解决方案。

3. 异步操作处理

  • 任务完成通知:当一个异步任务(如数据加载、文件上传)完成时,可以通过事件总线通知其他相关组件更新其状态或进行后续操作。
  • 操作结果广播:在复杂的操作中,多个组件可能需要知道操作结果,事件总线可以用来广播这些结果。

4. 模块化设计

  • 模块间通信:在大型应用中,不同的功能模块可能需要相互通信。事件总线允许模块之间解耦合,同时保持灵活性和可扩展性。
  • 组件库:在组件库中,各个组件可能需要响应全局事件或广播事件,事件总线是一种适合的实现方式。

5. 跨页面通信

  • 单页面应用:在单页面应用(SPA)中,事件总线可以用于跨视图或跨页面的事件通信。
  • Tab 之间通信:虽然现代浏览器提供了更复杂的跨页面通信机制(如 BroadcastChannel API),事件总线可以在同一页面内的多个组件间进行有效通信。

实现示例

使用 JavaScript 的 EventEmitter 实现事件总线

// eventBus.js
import EventEmitter from 'events';

const eventBus = new EventEmitter();

export default eventBus;

发送事件

// ComponentA.js
import React from 'react';
import eventBus from './eventBus';

function ComponentA() {
  const sendMessage = () => {
    eventBus.emit('message', 'Hello from ComponentA');
  };

  return <button onClick={sendMessage}>Send Message</button>;
}

export default ComponentA;

接收事件

// ComponentB.js
import React, { useEffect, useState } from 'react';
import eventBus from './eventBus';

function ComponentB() {
  const [message, setMessage] = useState('');

  useEffect(() => {
    const handleMessage = (msg) => {
      setMessage(msg);
    };

    eventBus.on('message', handleMessage);

    return () => {
      eventBus.off('message', handleMessage);
    };
  }, []);

  return <p>{message}</p>;
}

export default ComponentB;

注意事项

  • 性能:在高频率的事件发送和接收场景中,事件总线可能会导致性能问题,需要考虑优化方案。
  • 调试:由于事件总线在组件间传递数据不通过直接的 props 或 state,调试可能会变得更加复杂。
  • 命名冲突:确保事件名称具有唯一性,以避免命名冲突带来的问题。

事件总线是一种强大的工具,但也需要谨慎使用,确保其在应用中的使用符合最佳实践,并且不会引入不必要的复杂性。

事件总线涉及到什么设计模式呢?

事件总线(Event Bus)涉及的设计模式主要包括以下几种:

1. 观察者模式(Observer Pattern)

定义:观察者模式是一种行为设计模式,其中一个对象(称为主题或被观察者)维护一组依赖于它的对象(称为观察者)。当被观察者发生变化时,它会通知所有观察者,以便它们更新其状态。

在事件总线中的应用

  • 事件总线作为主题:事件总线充当了观察者模式中的“主题”,发布事件。
  • 组件作为观察者:订阅事件的组件充当“观察者”,当事件发生时,事件总线通知所有订阅者。

示例

class EventBus {
  constructor() {
    this.events = {};
  }

  on(event, listener) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(listener);
  }

  off(event, listener) {
    if (!this.events[event]) return;
    this.events[event] = this.events[event].filter(l => l !== listener);
  }

  emit(event, ...args) {
    if (!this.events[event]) return;
    this.events[event].forEach(listener => listener(...args));
  }
}

2. 发布/订阅模式(Publish/Subscribe Pattern)

定义:发布/订阅模式是一种消息传递模式,其中发布者发送消息到一个消息通道,而订阅者从该消息通道接收消息。发布者和订阅者之间不直接通信。

在事件总线中的应用

  • 发布者:在事件总线中,发布事件的部分充当“发布者”。
  • 订阅者:订阅事件的部分充当“订阅者”,它们通过事件总线接收消息。

示例

const eventBus = new EventBus();

eventBus.on('eventName', data => {
  console.log('Received event data:', data);
});

eventBus.emit('eventName', { key: 'value' });

3. 中介者模式(Mediator Pattern)

定义:中介者模式是一种行为设计模式,通过定义一个中介对象来封装一组对象之间的交互。中介者使得各个对象之间的通信通过中介对象进行,从而降低了它们之间的耦合度。

在事件总线中的应用

  • 事件总线作为中介者:事件总线充当中介者,负责协调各个组件或模块之间的通信和数据交换。

示例

class Mediator {
  constructor() {
    this.channels = {};
  }

  subscribe(channel, listener) {
    if (!this.channels[channel]) {
      this.channels[channel] = [];
    }
    this.channels[channel].push(listener);
  }

  publish(channel, data) {
    if (!this.channels[channel]) return;
    this.channels[channel].forEach(listener => listener(data));
  }
}

4. 消息传递模式(Message Passing Pattern)

定义:消息传递模式是一种设计模式,通过传递消息来实现对象之间的通信。这种模式允许系统中的不同部分通过发送和接收消息进行交互。

在事件总线中的应用

  • 事件总线作为消息通道:事件总线充当消息通道,通过发布和订阅机制实现消息传递。

示例

class MessageBus {
  constructor() {
    this.subscribers = {};
  }

  subscribe(channel, handler) {
    if (!this.subscribers[channel]) {
      this.subscribers[channel] = [];
    }
    this.subscribers[channel].push(handler);
  }

  publish(channel, message) {
    if (!this.subscribers[channel]) return;
    this.subscribers[channel].forEach(handler => handler(message));
  }
}

总结

  • 观察者模式:事件总线实现了主题-观察者关系,使得组件能够订阅和响应事件。
  • 发布/订阅模式:事件总线通过发布和订阅机制实现组件间的消息传递。
  • 中介者模式:事件总线作为中介者,协调不同组件之间的通信。
  • 消息传递模式:事件总线通过消息传递实现不同组件间的数据交换。

这些设计模式在事件总线中有机地结合在一起,使其成为一种有效的组件间通信和解耦工具。

转载自:https://juejin.cn/post/7398747326070702089
评论
请登录