Во Vue.js архитектура приложения строится вокруг компонентов, каждый
из которых изолирован по состоянию и логике. Стандартные способы
взаимодействия — props для передачи данных вниз и
emit для уведомлений вверх — хорошо работают в иерархии
«родитель → потомок». Однако при усложнении приложения появляются
следующие сложности:
props через промежуточные
уровни;Для решения этой проблемы используется паттерн Mediator, выступающий посредником между компонентами.
Mediator инкапсулирует логику взаимодействия между объектами, позволяя им не знать друг о друге напрямую. Компоненты не общаются между собой напрямую, а взаимодействуют через общий посредник.
Ключевые характеристики:
props и emit.Во Vue роль медиатора может выполнять:
Классический способ реализации Mediator во Vue — event bus.
// mediator.js
import { reactive } from 'vue'
export const mediator = {
events: reactive({}),
on(event, handler) {
if (!this.events[event]) {
this.events[event] = []
}
this.events[event].push(handler)
},
emit(event, payload) {
if (!this.events[event]) return
this.events[event].forEach(handler => handler(payload))
},
off(event, handler) {
if (!this.events[event]) return
this.events[event] =
this.events[event].filter(h => h !== handler)
}
}
Mediator хранит список событий и обработчиков, не зная ничего о компонентах-источниках и подписчиках.
import { mediator } from '@/mediator'
export default {
methods: {
saveUser(user) {
mediator.emit('user:saved', user)
}
}
}
Компонент сообщает о событии, не зная, кто и как его обработает.
import { mediator } from '@/mediator'
export default {
mounted() {
mediator.on('user:saved', this.handleUserSaved)
},
beforeUnmount() {
mediator.off('user:saved', this.handleUserSaved)
},
methods: {
handleUserSaved(user) {
console.log('Пользователь сохранён', user)
}
}
}
Компонент реагирует на событие, не зная источник.
Изоляция компонентов Компоненты не зависят друг от друга напрямую, что снижает риск побочных эффектов.
Централизация коммуникации Вся логика взаимодействия проходит через единый слой.
Гибкость масштабирования Добавление новых участников взаимодействия не требует изменений существующих компонентов.
Повышение тестируемости Mediator легко подменяется или мокируется в тестах.
Во Vue 3 Mediator удобно реализовывать через composable.
// useMediator.js
const listeners = new Map()
export function useMediator() {
function on(event, handler) {
if (!listeners.has(event)) {
listeners.set(event, [])
}
listeners.get(event).push(handler)
}
function emit(event, payload) {
if (!listeners.has(event)) return
listeners.get(event).forEach(h => h(payload))
}
function off(event, handler) {
if (!listeners.has(event)) return
listeners.set(
event,
listeners.get(event).filter(h => h !== handler)
)
}
return { on, emit, off }
}
Использование:
import { useMediator } from '@/useMediator'
const mediator = useMediator()
mediator.emit('modal:open')
mediator.on('modal:open', () => {
console.log('Открытие модального окна')
})
Composable-медиатор не привязан к компонентному дереву и может использоваться в любом месте приложения.
Mediator:
Глобальное состояние (Vuex, Pinia):
Mediator не заменяет store, а дополняет его, закрывая задачу событийной коммуникации.
Скрытые зависимости Компоненты перестают явно показывать, откуда приходят события.
Отладка Большое количество событий усложняет трассировку потока выполнения.
Перегрузка mediator-а При отсутствии дисциплины mediator может превратиться в «свалку» событий.
Для предотвращения проблем используется:
Рекомендуется использовать неймспейсы:
user:created
user:updated
modal:open
modal:close
form:validated
Такой подход:
В крупных приложениях Mediator выделяется в отдельный уровень:
UI Components
↓
Mediator
↓
Business Logic / Services
Компоненты работают только с mediator-ом, не взаимодействуя напрямую ни друг с другом, ни с сервисами.
Mediator — архитектурный инструмент, который при осознанном применении значительно упрощает коммуникацию компонентов во Vue.js и делает кодовую базу устойчивой к росту сложности.