Fredrik Mäkilä
3 min readI recently wanted to create a basic marketing website for my consulting firm. For this, I needed to create some shared state. I am using Nuxt and adding Vuex support to achieve this is really easy. It did feel like overkill, however, so I started looking for options.
I was going to implement dark mode through a toggle, and I wanted to make use of Tailwind's dark mode to do so. That meant adding a dark
class somewhere at the root of the dom tree. I was using Typescript (perhaps also a bit overkill, but I like it), so to set up Vuex is a bit more involved to get it to play well with Nuxt. I wanted two-way data flow so that the toggle could be updated in case the media query for dark mode preference changes. This would have been really straight forward with Vuex, but I wanted something simpler.
An event bus is a pretty neat way to pass data between components using events. The basic idea is that a component can emit an event on the event bus and any number of other components can then subscribe to that event.
import Vue from "vue";
const eventBus = new Vue();
export default eventBus;
<script>
import eventBus from './eventBus'
export default {
methods: {
send () {
eventBus.$emit('message', 'hello')
}
}
}
</script>
<script>
import eventBus from './eventBus';
export default {
mounted () {
eventBus.$on('message', message => {
console.log(message); // 'hello'
})
}
}
</script>
The biggest benefit from this technique can be had when two components do not have a direct parent and child relationship. The example above is only possible in Vue 2.x. For Vue 3 you can use something like Mitt to achieve the same thing.
The downside, and the reason why I started to look further than this technique, is that it is designed for communication one way. For my use case, that was a requirement so further research was needed.
Reactivity in Vue is a funny thing and can sometimes be tricky to get right. To turn any object reactive we can use Vue.observable (renamed reactive in Vue 3).
import Vue from "vue";
const state = {
message: "",
edits: 0
};
export default Vue.observable({
get message() {
return state.message;
},
set message(message) {
state.message = message;
state.edits++;
}
});
By using the get and set syntax it is possible to introduce side effects whenever some property changes, which is quite neat. The example above can simply be used with v-model
, making it really easy to use.
<template>
<input v-model="state.message" />
</template>
<script>
import state from "../state";
export default {
data() {
return {
state,
};
},
};
</script>
These are some alternatives to Vuex. For state that needs to be immediately reactive, I prefer the Vue.observable approach. In cases where an event triggers, for example, a backend request I think the event bus is useful. Better than either of these approaches is when components can communicate with each other as a parent and child. When that is not possible using Vuex or these techniques is a good solution.
Full examples can be found in the following Codesandbox.