Медиаторы и Event Bus

В архитектуре современных веб-приложений взаимодействие между компонентами часто становится узким местом. Для управления событиями и передачи данных между разными частями приложения используются паттерны Медиатор и Event Bus. Preact, как легковесная альтернатива React, не накладывает строгих правил, позволяя гибко интегрировать эти подходы.


Паттерн Медиатор

Медиатор служит централизованным объектом, через который компоненты обмениваются информацией. Он помогает снизить связанность между компонентами и упрощает поддержку крупного приложения.

Основные принципы:

  1. Централизация взаимодействий Все компоненты отправляют и получают сообщения через медиатор, что устраняет прямые зависимости между ними.

  2. Слабая связанность Компоненты не знают о существовании друг друга. Они взаимодействуют только с медиатором.

  3. Управление событиями Медиатор управляет подпиской и обработкой событий, обеспечивая упорядоченность и предсказуемость работы.

Пример реализации

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

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

  unsubscribe(channel, fn) {
    if (!this.channels[channel]) return;
    this.channels[channel] = this.channels[channel].filter(subscriber => subscriber !== fn);
  }

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

export const mediator = new Mediator();

В Preact использование медиатора происходит через вызовы методов publish и subscribe. Например, один компонент может отправить событие о новом элементе:

import { mediator } from './mediator';

function AddItemButton() {
  const handleClick = () => {
    mediator.publish('itemAdded', { id: Date.now(), name: 'Новый элемент' });
  };

  return <button onCl ick={handleClick}>Добавить элемент</button>;
}

Другой компонент подписывается на событие:

import { useEffect, useState } from 'preact/hooks';
import { mediator } from './mediator';

function ItemList() {
  const [items, setItems] = useState([]);

  useEffect(() => {
    const handleNewItem = (item) => setItems(prev => [...prev, item]);
    mediator.subscribe('itemAdded', handleNewItem);
    return () => mediator.unsubscribe('itemAdded', handleNewItem);
  }, []);

  return (
    <ul>
      {items.map(item => <li key={item.id}>{item.name}</li>)}
    </ul>
  );
}

Event Bus

Event Bus — это вариация медиатора, ориентированная на события. Он часто используется для глобального обмена событиями без необходимости передачи их через пропсы.

Основные характеристики:

  • Глобальность События доступны в любой части приложения, что упрощает коммуникацию между разнородными компонентами.

  • Производительность Event Bus минимизирует лишние перерендеры, так как обработка событий происходит вне дерева компонентов.

  • Поддержка асинхронных операций Event Bus удобно использовать для уведомлений об успешных действиях, ошибок или обновлений состояния.

Пример Event Bus на Preact

import { EventEmitter } from 'events';

export const eventBus = new EventEmitter();

Подписка на событие:

import { useEffect, useState } from 'preact/hooks';
import { eventBus } from './eventBus';

function NotificationList() {
  const [notifications, setNotifications] = useState([]);

  useEffect(() => {
    const handleNotification = (message) => setNotifications(prev => [...prev, message]);
    eventBus.on('notify', handleNotification);
    return () => eventBus.off('notify', handleNotification);
  }, []);

  return (
    <ul>
      {notifications.map((msg, idx) => <li key={idx}>{msg}</li>)}
    </ul>
  );
}

Отправка события:

import { eventBus } from './eventBus';

function NotifyButton() {
  const notify = () => {
    eventBus.emit('notify', 'Новое уведомление');
  };

  return <button onCl ick={notify}>Уведомить</button>;
}

Сравнение Медиатора и Event Bus

Характеристика Медиатор Event Bus
Централизация Высокая Средняя
Слабая связанность Да Да
Глобальные события Не обязательно Да
Управление подписками Через объект Через события EventEmitter
Поддержка асинхронности Реализуется вручную Встроенная через emit/on

Рекомендации по использованию

  • Медиатор лучше подходит для локальных сетей взаимодействий между компонентами в рамках одного модуля.
  • Event Bus эффективен для глобальных уведомлений и событий, которые должны быть доступны в разных частях приложения.
  • В больших Preact-приложениях возможна комбинация обоих подходов: Event Bus для глобальных уведомлений и медиаторы для внутренних связей модулей.

Особенности интеграции с Preact

  • Использование хуков useEffect и useState позволяет управлять подписками на события с автоматическим снятием слушателей при размонтировании компонентов.
  • Event Bus не вызывает лишние рендеры, если события не изменяют локальное состояние компонента напрямую.
  • Медиатор позволяет создавать сложные цепочки событий, сохраняя управляемость и тестируемость кода.

Эти подходы повышают масштабируемость приложения, делают архитектуру гибкой и позволяют строить чистую коммуникацию между компонентами без прямой зависимости.