В одном из моих последних внештатных проектов мой клиент предпочитает 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 .