# Удалено API для событий
кардинальное изменение

# Обзор

Методы экземпляров $on, $off и $once были удалены. Экземпляры компонентов больше не реализуют интерфейс эмиттера событий.

# Синтаксис в 2.x

В версии 2.x, экземпляр Vue можно было использовать для вызова обработчиков, которые привязывались императивно через API эмиттера событий ($on, $off и $once). Это можно было использовать для реализации шины событий и создания глобальных прослушивателей событий, используемых во всем приложении:

// eventBus.js

const eventBus = new Vue()

export default eventBus
1
2
3
4
5
// ChildComponent.vue
import eventBus from './eventBus.js'

export default {
  mounted() {
    // привязка слушателя к шине событий
    eventBus.$on('custom-event', () => {
      console.log('Вызвано пользовательское событие!')
    })
  },
  beforeDestroy() {
    // удаление слушателя из шины событий
    eventBus.$off('custom-event')
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ParentComponent.vue
import eventBus from './eventBus.js'

export default {
  methods: {
    callGlobalCustomEvent() {
      eventBus.$emit('custom-event') // если ChildComponent примонтирован, то появится сообщение в консоли
    }
  }
}
1
2
3
4
5
6
7
8
9
10

# Что изменилось в 3.x

Из экземпляра полностью удалены методы $on, $off и $once. Метод $emit всё ещё является частью существующего API, так как он используется для запуска обработчиков событий, декларативно прикреплённых к родительским компонентам.

# Стратегия миграции

Флаг сборки для миграции: INSTANCE_EVENT_EMITTER

Во Vue 3 больше нет возможности использовать эти API чтобы прослушивать генерируемые события в компоненте в других компонентах, для подобного применения нет пути миграции.

# События корневого компонента

Статические слушатели событий можно добавить в корневой компонент, передавая их в качестве входных параметров в createApp:

createApp(App, {
  // Прослушивание события 'expand'
  onExpand() {
    console.log('expand')
  }
})
1
2
3
4
5
6

# Шина событий

Паттерн шины событий можно использовать с помощью внешней библиотеки, реализующей интерфейс эмиттера событий, например mitt (opens new window) или tiny-emitter (opens new window).

Например:

// eventBus.js
import emitter from 'tiny-emitter/instance'

export default {
  $on: (...args) => emitter.on(...args),
  $once: (...args) => emitter.once(...args),
  $off: (...args) => emitter.off(...args),
  $emit: (...args) => emitter.emit(...args)
}
1
2
3
4
5
6
7
8
9

Это обеспечивает такой же API событий, как и во Vue 2.

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

  • Входные параметры и события должны быть основным выбором для общения между родителями и дочерними компонентами. Соседние компоненты могут общаться через своего родителя.
  • Provide и inject позволяют компоненту взаимодействовать с содержимым своего слота. Это полезно для тесно связанных компонентов, которые всегда используются вместе.
  • provide/inject также можно использовать для связи компонентов на большом расстоянии. Это может помочь избежать «прокидывания» входных параметров, когда потребуется передать данные через несколько уровней компонентов, которым эти данные не нужны.
  • Прокидывание входных параметров можно также избежать путём рефакторинга для использования слотов. Если промежуточный компонент не нуждается в этих входных параметрах, это может указывать на проблему с разделением ответственности. Внедрение слота в этот компонент позволит родительскому предоставлять содержимое для слота самостоятельно, так что входной параметр можно будет передать напрямую, без участия промежуточного компонента.
  • Глобальное управление состоянием, например с помощью Vuex (opens new window).