For a while now, I have relied on a global event bus in Vue - creating it as const bus = new Vue()
. It works well, but managing subscriptions can get tedious at times.
Imagine subscribing to an event in a component:
mounted() {
bus.$on('some.event', callback)
}
In order to properly dispose of the callback, I need to keep track of it and handle its removal in beforeDestroy
. While using a global mixin can simplify this process, things get more complex when dealing with subscriptions made in both mounted
and activated
callbacks due to my usage of <keep-alive>
.
Considering these challenges, I decided to experiment with Vuex for managing the event system. The approach I came up with is detailed below.
Everything seems to work smoothly when publishing objects or arrays. However, primitive data doesn't trigger reactivity even when wrapped in an outer object like { data: 123 }
I'm open to suggestions for better ways to notify subscribers about events. So far, the internal notify
method seems risky to rely on.
eventstore.js
import Vue from 'vue'
const state = {
events: {}
}
const actions = {
publish({commit}, payload) {
commit('publish_event', payload)
}
}
const mutations = {
publish_event(state, payload) {
if(!state.events[payload.key]) {
Vue.set(state.events, payload.key, { data: payload.data })
} else {
state.events[payload.key] = { data: payload.data }
}
}
}
const getters = {
events: (state) => state.events
}
export default {
state,
actions,
mutations,
getters
}
globalmixin.js
methods: {
publish(key, data) {
this.$store.dispatch('publish', { key, data })
}
}
somecomponent.vue
function mapEventGetters(eventKeys) {
return _.reduce(eventKeys, (result, current) => {
result[current] = function() {
return _.get(this, `$store.getters.events.${current}.data`)
}
return result
}, {})
}
computed: {
...mapEventGetters(['foo_bar'])
},
watch: {
'foo_bar'(value) {
console.log(`foo_bar changed to ${value}`)
}
}