An Introduction to Vuex

Vuex is the official state management library for Vue. A handy metaphor is that Vuex is to Vue as Redux is to React. If you already know Redux, Vuex will seem familiar, just with slightly different terminology. In this article, you'll learn the basics of Vuex from standalone Node.js scripts, no browser required.

First, to get started, you should install vue, vuex, and vue-server-renderer from npm. Here's how you import these libraries:

const { renderToString } = require ( 'vue-server-renderer' ).createRenderer(); const Vuex = require ( 'vuex' ); Vue.use(Vuex);

Next, let's define a template that displays a single number count that's stored in Vuex. This script has 4 steps:

Create a Vuex store. To create a Vuex store, you need to define state, mutations, and actions. Create a Vue app that's wired up to use the Vuex store. Render the app using vue-server-renderer. Dispatch an action and re-render the app using vue-server-renderer.

const state = { count : 0 }; const mutations = { increment : ( state ) => { ++state.count; }, decrement : ( state ) => { --state.count; } }; const actions = { increment : ( { commit } ) => commit( 'increment' ), decrement : ( { commit } ) => commit( 'decrement' ) }; const store = new Vuex.Store({ state, mutations, actions }); const app = new Vue({ store, template: '<div>{{$store.state.count}}</div>' }); await renderToString(app); store.dispatch( 'increment' ); store.state.count; await renderToString(app); assert.equal( await renderToString(app), '<div data-server-rendered="true">1</div>' );

If you're coming from Redux, the concepts of state and action in Vuex are equivalent to states and actions in Redux. You can think of a mutation as being equivalent to a reducer.

Async Actions

One key difference between actions and mutations is that actions can be asynchronous, whereas mutations must be synchronous. Making state changes in separate synchronous mutations enables better debugging and better devtools. Actions, however, can be async. For example, your increment action can be async as shown below.

const state = { count : 0 }; const mutations = { increment : ( state ) => { ++state.count; }, decrement : ( state ) => { --state.count; } }; const actions = { increment : async ({ commit }) => { await new Promise ( resolve => setTimeout(resolve, 100 )); commit( 'increment' ); } }; const store = new Vuex.Store({ state, mutations, actions }); const app = new Vue({ store, template : '<div>{{$store.state.count}}</div>' }); await store.dispatch( 'increment' ); await renderToString(app); assert.equal( await renderToString(app), '<div data-server-rendered="true">1</div>' );

One important caveat is that Vuex doesn't handle errors in async actions for you. If an async action throws an error, you'll get an unhandled promise rejection unless you explicitly handle the error using .catch() or async/await.

const actions = { increment : async () => { await new Promise ( resolve => setTimeout(resolve, 100 )); throw new Error ( 'Oops' ); } }; const store = new Vuex.Store({ state, mutations, actions }); const err = await store.dispatch( 'increment' ).catch( err => err); err.message;

More Vue Tutorials