Stimulus — это легковесный фреймворк, который организует поведение
веб-страниц через контроллеры, связывая их с DOM с помощью
атрибутов data-controller, data-action и
data-target. В сложных приложениях часто возникает
необходимость координации нескольких контроллеров, обмена данными и
событий между ними. Здесь на помощь приходит паттерн
Медиатор, обеспечивающий централизованное управление
взаимодействием компонентов без их тесной связки друг с другом.
Медиатор выступает как посредник, который принимает события от одного контроллера и уведомляет другие контроллеры о необходимости действия. Основные преимущества:
В контексте Stimulus медиатор обычно реализуется как отдельный JavaScript-модуль, который экспортирует методы подписки, публикации событий и управления состоянием.
Простейшая структура медиатора может выглядеть так:
export const mediator = {
channels: {},
subscribe(channel, callback) {
if (!this.channels[channel]) {
this.channels[channel] = [];
}
this.channels[channel].push(callback);
},
publish(channel, data) {
if (!this.channels[channel]) return;
this.channels[channel].forEach(callback => callback(data));
},
unsubscribe(channel, callback) {
if (!this.channels[channel]) return;
this.channels[channel] = this.channels[channel].filter(cb => cb !== callback);
}
};
Ключевые моменты:
channels хранит все подписки.subscribe регистрирует обработчики для
определённого канала.publish рассылает данные всем подписчикам.unsubscribe позволяет удалять обработчики,
предотвращая утечки памяти.Контроллер Stimulus может использовать медиатор для уведомления других компонентов или реагирования на внешние события:
import { Controller } from "@hotwired/stimulus";
import { mediator } from "../mediator";
export default class extends Controller {
connect() {
mediator.subscribe('user:updated', this.updateUser.bind(this));
}
disconnect() {
mediator.unsubscribe('user:updated', this.updateUser.bind(this));
}
updateUser(data) {
this.element.textContent = `Имя пользователя: ${data.name}`;
}
notifyChange() {
const newName = this.element.querySelector('input').value;
mediator.publish('user:updated', { name: newName });
}
}
Особенности подключения:
connect и disconnect обеспечивают
корректное управление подписками.Медиатор поддерживает работу с различными типами событий через каналы:
mediator.subscribe('cart:itemAdded', (item) => {
console.log('Товар добавлен в корзину:', item);
});
mediator.subscribe('cart:itemRemoved', (itemId) => {
console.log('Товар удален из корзины, ID:', itemId);
});
Контроллеры могут публиковать события на соответствующих каналах:
mediator.publish('cart:itemAdded', { id: 1, name: 'Ноутбук' });
mediator.publish('cart:itemRemoved', 1);
Такое раздельное использование каналов позволяет структурировать события по контексту и избегать конфликтов между контроллерами.
Для более сложных приложений медиатор может быть расширен:
Пример фильтрующего обработчика:
mediator.subscribe('user:updated', (data) => {
if (data.role === 'admin') {
console.log('Обновлён администратор:', data.name);
}
});
Stimulus предоставляет события жизненного цикла connect,
disconnect и initialize, которые идеально
подходят для управления подписками на медиатор:
initialize: подготовка внутреннего состояния
контроллера.connect: регистрация подписок на медиатор.disconnect: отписка от всех каналов для предотвращения
утечек памяти.Следование этому паттерну обеспечивает устойчивую архитектуру, где контроллеры остаются независимыми и легко поддерживаемыми, даже если количество компонентов растёт.
Использование медиатора в Stimulus позволяет строить сложные интерфейсы без усиления взаимной зависимости контроллеров, сохраняя лёгкость и читабельность кода.