Understanding Vuex

Recently, I try to use Vuex to do some small projects. After some studies, I find a better way for myself to understand and use it. Here, I just put them done in my blog for later reading. If you find any grammar mistakes or misleading opinions, please let me kown. Thank you.


define a store

First define a store. A store should contain 4 parts, they are

  1. state
  2. mutaions
  3. actions
  4. getters

after define these 4 parts, combine them by using Vue.Store().

State is an Object. It contains all the data information of your app. Each Vuex instance is just a single state tree.

mutations are operations that actually mutates state. And state can only be changed by mutations. Each mutaion handler gets the entire state tree as the first argument, followed by additional payload arguments. Mutations must be synchronous and be recorded by plugins for debugging purposes.

actions are functions that causes side effects and handle your logical process . Unlike mutations, you can involve asynchronous operations.

getters are functions.

let’s see an counter example.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

//define root state object.
const state = {
count: 0
}

// define mutaions
const mutations = {
increment (state) {
state.count++
},
decrement (state) {
state.count--
}
}

// define actions
const actions = {
increment: ({ commit }) => commit('increment'),
decrement: ({ commit }) => commit('decrement'),
incrementIfOdd ({ commit, state }) {
if ((state.count + 1) % 2 === 0) {
commit('increment')
}
},
incrementAsync ({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('increment')
resolve()
}, 1000)
})
}
}

// define getters
const getters = {
evenOrOdd: state => state.count % 2 === 0 ? 'even' : 'odd'
}

// A Vuex instance is created by combining the state, mutations, actions,
// and getters.
export default new Vuex.Store({
state,
getters,
actions,
mutations
})

The above example comes from https://github.com/vuejs/vuex. You can see that all the four parts are been written in one file, which is bad for big and complex projects. To avoid this, just split them, and then combine them together.


define entry

You may have an root element in your app. Just set the store and the view component to the root element. In this way , your store will be available in any place of your application.

1
2
3
4
5
6
7
8
9
import Vue from 'vue'
import Counter from './Counter.vue'
import store from './store'

new Vue({
el: '#app',
store,
render: h => h(Counter)
})

Then, you just need to include this file in your index.html. Shared.js is common code that you extract by webpack.
bundle.js includes all the code that you need in this app.

1
2
3
4
5
<body>
<div id="app"></div>
<script src="shared.js"></script>
<script src="bundle.js"></script>
</body>

write your view component

After defining the entry, We can begin write our component. Notice that you can use your Store Object now.

Use $store.state.count to get your state info. And by functions like mapGetters and mapActions ,you can get the functions that you have just defined in store a moment ago.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<template>
<div id="app">
Clicked: {{ $store.state.count }} times, count is {{ evenOrOdd }}.
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="incrementIfOdd">Increment if odd</button>
<button @click="incrementAsync">Increment async</button>
</div>
</template>

<script>
import { mapGetters, mapActions } from 'vuex'

export default {
computed: mapGetters([
'evenOrOdd'
]),
methods: mapActions([
'increment',
'decrement',
'incrementIfOdd',
'incrementAsync'
])
}
</script>

run the app

We have just used ES2015 and vue, so it’s necessary to use webpack compiling all the code.

Actually, I am not very skilled in webpack. So I keep them down, and give some small introductions for later checking out.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
const webpack = require('webpack');
const path = require('path');

module.exports = {
entry: './app.js',

output: {
path: __dirname,
filename: 'bundle.js'
},

module: {
rules: [
{test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader'},
{test: /\.vue$/, loader: 'vue-loader'}
]
},

plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'shared',
filename: 'shared.js'
}), //get share.js

new webpack.DefinePlugin({ // 编译时配置的全局变量
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
}),

new webpack.HotModuleReplacementPlugin(), //热更新插件
new webpack.NoEmitOnErrorsPlugin() //不触发错误,即编译后运行的包正常运行
]
}

After make the webpack.config,let’s start the server. Here, we can use express to build a little server.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
const express = require('express')
const webpack = require('webpack')
const webpackConfig = require('./webpack.config')
const webpackHotMiddleware = require('webpack-hot-middleware')
const webpackDevMiddleware = require('webpack-dev-middleware')
const opn = require('opn')


const app = express()
const port = process.env.PORT || 8080
const autoOpenBrowser = true //you can load the config from your config file.
const compiler = webpack(webpackConfig)

app.use(webpackDevMiddleware(compiler, {
stats: {
colors: true,
chunks: false
}
}))

app.use(webpackHotMiddleware(compiler))
app.use(express.static(__dirname))

module.exports = app.listen(port, (err) => {
if(err) return

console.log(`Server listening on http://localhost:${port}, Ctrl+C to stop`)

if(autoOpenBrowser) {
opn(`http://localhost:${port}`)
}
})

npm package opn was used to force open the browser.