I’ve created small library that I use for Vue 3 apps
// Factory: createEventBus
const createEventBus = function () {
const state = {
listeners: {}
}
const methods = {
// addEventListener
$on(eventType, callback) {
if (!Array.isArray(state.listeners[eventType])) {
state.listeners[eventType] = []
}
if (state.listeners[eventType].indexOf(callback) === -1)
state.listeners[eventType].push(callback)
},
// removeEventListener
$off(eventType, callback) {
if (Array.isArray(state.listeners[eventType])) {
const index = state.listeners[eventType].indexOf(callback)
if (index !== -1) state.listeners[eventType].splice(index, 1)
}
},
// dispatchEvent
$emit(eventType, data) {
if (Array.isArray(state.listeners[eventType]))
state.listeners[eventType].forEach(cb => cb(data))
},
// reset listeners
$destroy() {
state.listeners = {}
}
}
return methods
}
// Class: EventBus
const EventBus = function () {
Object.assign(this, createEventBus())
}
export default createEventBus
export { EventBus }
How to use
Factory way
import createEventBus from './createEventBus'
// create the product (from Factory)
const eventBus = createEventBus()
export default eventBus
import eventBus from './eventBus'
// # addEventListener
eventBus.$on('message', console.log.bind(console, 'message:'))
const temporaryEvent = console.log.bind(console, 'temporary:')
eventBus.$on('temporary', temporaryEvent)
// # removeEventListener
eventBus.$off('temporary', temporaryEvent)
// # dispatchEvent
eventBus.$emit('message', 'Hello world')
// -> message: Hello world
eventBus.$emit('message', { name: 'Anna', age: 21 })
// -> {name: 'Anna', age: 21}
// # destroy events
eventBus.$destroy()
eventBus.$emit('message', 'This should not log after destroy')
// ->
Class way
import { EventBus } from './createEventBus'
// instantiate EventBus
const eventBus = new EventBus()
export default eventBus
import { EventBus } from './createEventBus'
import eventBus from './eventBus'
// # Check instance
console.log(eventBus instanceof EventBus)
// -> true
// # addEventListener
eventBus.$on('message', console.log.bind(console, 'message:'))
const temporaryEvent = console.log.bind(console, 'temporary:')
eventBus.$on('temporary', temporaryEvent)
// # removeEventListener
eventBus.$off('temporary', temporaryEvent)
// # dispatchEvent
eventBus.$emit('message', 'Hello world')
// -> message: Hello world
eventBus.$emit('message', { name: 'Anna', age: 21 })
// -> {name: 'Anna', age: 21}
// # destroy events
eventBus.$destroy()
eventBus.$emit('message', 'This should not log after destroy')
// ->
Vue Option API way
No, I don’t like over-hyped Composition API
<script>
import createEventBus from './createEventBus'
export default {
// LifeCycle Hooks
beforeCreate() {
// create the product (from Factory)
const eventBus = eventEventBus()
const {
$on: $$on,
$off: $$off,
$emit: $$emit // Vue has internal $emit, change to $$emit
} = eventBus
// assign to Proxy Object
Object.assign(
this,
{ eventBus },
{
$$on,
$$off,
$$emit
}
)
},
mounted() {
this.intervalID = setInterval(() => {
this.$$emit('tick', 'Tick Tock')
}, 1e3)
},
beforeUnmount() {
// destroy IntervalID
clearInterval(this.intervalID)
// destroyes all eventBus event listeners
this.eventBus.$destroy()
}
}
</script>
import { createApp } form 'vue'
import HelloWorld from './HelloWorld.vue'
const template = document.createElement('template')
const app = createApp(vueOjbject, props)
const proxy = app.mount(template) // Proxy Object
// attach addEventListener
proxy.$$on('tick', data => {
console.log('on tick:', data)
})
// -> on tick: Tick Tock
// -> on tick: Tick Tock
// -> on tick: Tick Tock