В одном из моих последних внештатных проектов мой клиент предпочитает Vue.js , который в последнее время очень популярен на стороне внешнего интерфейса. Итак, я нырнул в Vue . Я могу сказать, что это очень практично и эффективно. Кроме того, когда мы сравниваем его с другими доминирующими конкурентами, такими как Angular и Aurelia, мы можем легко заметить, что у Vue очень маленькая кривая обучения.
Однако мне не потребовалось много времени, чтобы почувствовать, что мой код становится неуправляемым. Это не было большим сюрпризом для меня, потому что это часто компромисс с динамически типизированными языками.
Сегодня я собираюсь показать эффективный способ использования глобальных событий в Vue.
Простой автобус для мероприятий в Vue
Типичный способ реализации глобальной шины событий в Vue — просто использование самого  Vue объекта:
JavaScript
xxxxxxxxxx
1
// create a Vue instance somewhere you can access globally
2
let eventBus = new Vue()
3
 
4
// register an event handler
5
eventBus.$on("onAppStarted", () => console.log("App Started!"))
6
 
7
// publish an event
8
eventBus.$emit("onAppStarted")
Супер просто, правда?
Тем не менее, есть несколько проблем, скрытых под этим классным динамическим синтаксисом.
Вы можете также как:
Как и почему мы переехали в Vue.js .
Проблема, исходящая из строк
Поскольку в нашем приложении более двух строк, рано или поздно мы начинаем обращать внимание на то, какие компоненты публикуются, а какие их слушают другие.
Таким образом, мы можем представить, как сложно идентифицировать простую опечатку в имени события на основе строки, особенно в большом проекте:
JavaScript
x
1
eventBus.$on("onApStarted", () => {
2
  // notice the typo in event name, it should be "onAppStarted"  
3
})
Параметры неявного события
Это еще одна проблема, которую мы должны заметить, потому что мы не определяем никакого соответствующего типа или интерфейса для нашего события — только Бог знает, что и сколько параметров может быть в нашем событии.
Чтобы идентифицировать их, мы должны сделать такие тесты:
JavaScript
xxxxxxxxxx
1
eventBus.$on("onAppStarted", (args) => {
2
  args.forEach(e => console.log(e))    
3
})
Правильное решение исходит от ES6 +
Как поклонник статически типизированного мира Java, я предпочитаю четко использовать типы, если это не слишком необычно для конкретного языка. Таким образом, я покажу решение, позволяющее избавиться от этих имен событий на основе строк, используя возможности, которые предлагает ECMAScript 6 и более поздние версии .
Определение типов событий
Давайте создадим отдельный файл для определения наших типов событий:
JavaScript
xxxxxxxxxx
1
/**
2
* Event type to publish when app loads
3
* ProducedBy: components/preload.js
4
* ConsumedBy: App.vue, views/MainPanel.vue
5
**/
6
export class AppStartEvent {
7
 
8
  constructor(){
9
    // An event type with no arguments
10
  }
11
 
12
}
13
 
14
/**
15
* Event type to publish when code changes
16
* ProducedBy: views/CodePanel.vue
17
* ConsumedBy: views/MainPanel.vue
18
* @param {object} editor   editor instance
19
* @param {string} code     changed code value inside the editor
20
**/
21
export class CodeChangeEvent {
22
 
23
  constructor(editor, code){
24
    this.editor = editor
25
    this.code = code
26
  }
27
 
28
}
Как мы заметили, явное определение классов и параметров типов событий в конструкторе дает нам большую читабельность.
Хотя это и не обязательно, мы рекомендуем держать комментарии в актуальном состоянии. Это позволяет отслеживать компоненты, которые имеют дело с определенным типом события.
Импорт типов событий
Чтобы использовать наши события на основе типов, мы можем легко импортировать их в наши компоненты:
JavaScript
xxxxxxxxxx
1
import {AppStartEvent, CodeChangeEvent} from "@/app-events"
Поскольку мы явно указываем каждый тип события, который нам нужен, это дает нам еще одно важное преимущество: мы можем легко определить, какие события участвуют в компоненте.
Регистрация события
Чтобы зарегистрировать наше событие, мы просто используем наши типы событий и их статические  name свойства:
JavaScript
xxxxxxxxxx
1
import {AppStartEvent} from "@/app-events"
2
 
3
eventBus.$on(AppStartEvent.name, () => console.log("App Started!"))
Кроме того, мы можем ожидать сам экземпляр типа события как один аргумент вместо более чем одного аргумента:
JavaScript
xxxxxxxxxx
1
import {AppStartEvent, CodeChangeEvent} from "@/app-events"
2
 
3
// we can access the event type instance as a single argument
4
eventBus.$on(AppStartEvent.name, event => console.log(event))
5
 
6
// also can access to event parameters
7
eventBus.$on(CodeChangeEvent.name, event => {
8
  console.log(event.editor)  
9
  console.log(event.code)  
10
})
Публикация события
Теперь мы можем опубликовать наши события, просто создав новый экземпляр этого типа события:
JavaScript
xxxxxxxxxx
1
// no parameters
2
eventBus.$emit(AppStartEvent.name, new AppStartEvent())
3
 
4
// with parameters
5
eventBus.$emit(CodeChangeEvent.name, new CodeChangeEvent(editor, "some code here..."))
Реализация класса Wrapper
Конечно, мы можем приступить к определению класса  EventBusи обернуть основные методы  Vue экземпляра:
JavaScript
xxxxxxxxxx
1
class EventBus {
2
 
3
  $eventbus = new Vue()
4
 
5
  listen (eventClass, handler) {
6
    this.$eventBus.$on(eventClass.name, handler)
7
  }
8
 
9
  publish (event) {
10
    this.$eventBus.$emit(event.constructor.name, event)
11
  }
12
 
13
}
Поэтому мы можем использовать его более практичным способом:
JavaScript
xxxxxxxxxx
1
// register an event handler
2
EventBus.listen(AppStartEvent, () => console.log("App Started!"))
3
 
4
// publish an event
5
EventBus.publish(new AppStartEvent())
Использование в качестве плагина
Кроме того, мы можем предпочесть использовать наш  EventBusкак Vue Plugin:
JavaScript
xxxxxxxxxx
1
export default {
2
 
3
  $eventBus: null,
4
 
5
  install (Vue, options) {
6
    this.$eventBus = new Vue()
7
  },
8
 
9
  listen (eventClass, handler) {
10
    this.$eventBus.$on(eventClass.name, handler)
11
  },
12
 
13
  listenOnce (eventClass, handler) {
14
    this.$eventBus.$once(eventClass.name, handler)
15
  },
16
 
17
  remove (eventClass, handler) {
18
    if (handler) {
19
      this.$eventBus.$off(eventClass.name, handler)
20
    } else {
21
      this.$eventBus.$off(eventClass.name)
22
    }
23
  },
24
 
25
  removeAll () {
26
    this.$eventBus.$off()
27
  },
28
 
29
  publish (event) {
30
    this.$eventBus.$emit(event.constructor.name, event)
31
  }
32
 
33
}
Конечно, чтобы иметь возможность использовать плагин, мы должны импортировать и зарегистрировать его в нашем  Vueэкземпляре:
JavaScript
xxxxxxxxxx
1
import EventBus from '@/plugin/vue-event-bus'
2
 
3
Vue.use(EventBus)
Следовательно, мы можем просто импортировать и использовать наши  EventBusв любом другом компоненте Vue:
JavaScript
xxxxxxxxxx
1
import EventBus from '@/plugin/vue-event-bus'
2
import {AppStartEvent} from "@/app-events"
3
 
4
// register an event handler
5
EventBus.listen(AppStartEvent, () => console.log("App Started!"))
6
 
7
// publish an event
8
EventBus.publish(new AppStartEvent())
в заключение
В этом коротком руководстве я объяснил, как реализовать глобальные события на основе типов и использовать их в Vue.
Вы можете найти код примера плагина на GitHub .