Vuex简介

Vuex简介

最近的项目中可能会用到vuex,复习一下关于这段的内容。

什么是Vuex?

抛开官网上晦涩的解释,个人理解是Vuex是一个前端的状态”数据库”,它可以让各组件都能共享这个单例,触发行为更新视图。这张图可以看出这个关系:

flows]

Vuex的核心是 store 。在其中包含了应用中的大部分状态。具有以下两个特点:

  1. 状态储存响应式。状态修改时响应组件相应组件也能得到更新。
  2. 不能直接修改store中的状态。唯一的方法是显式地提交。

为什么要使用Vuex?

当多个组件共享同一个状态时,单向数据流的特性反而会形成一种负担,多个视图依赖于同一个状态,相反地,多个视图的行为需要改变同一个状态。前者是多层嵌套的组件,不能在兄弟组件之间状态传递,而后者使用eventBus或者父子组件直接引用。

以上的问题如果采用这些解决方法都难以维护,如果把组件的共享状态抽取出来,以单例模式进行管理,在各组件之间共享,那么问题就迎刃而解了。

如何使用Vuex?

以官网上的记数应用例子举例,前置条件为Vue脚手架。文件结构如下:

.
├── build                           
├── ......
├── src                             // 源码目录
│   ├── App.vue                     // 页面入口文件
│   ├── assets                      
│   │   └── logo.png
│   ├── store                     
│   │   └── store.js
│   ├── components                  
│   │   └── Count.vue   
│   └── main.js                     
.

component/Count.vue为主组件,新建store/store.js作为store容器

component/Count.vue

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
<template>
<div>
<result></result>
<opbutton></opbutton>
</div>
</template>
<script>
import store from '../store/store'
export default{
store,
components: {
result: {
template: `
<div>
{{ $store.state.count }}
</div>
`
},
opbutton: {
template: `
<div>
<input type="button" value="+">
<input type="button" value="-">
</div>
`
}
}
}
</script>

store/store.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const state = {
count: 0
}

const VuexStore = new Vuex.Store({
state
})

export default VuexStore

入口文件main.js中

1
2
3
4
5
6
7
8
9
10
//...
import storeConfig from './store/store'
import Count from './components/Count'

new Vue({
el: '#app',
render: h => h(Count),
template: '<Count/>',
components: { Count }
})

经过以上的步骤可以得到一个静态无点击事件的计数器页面。然后为按钮添加点击事件。需要在store/store.js容器中写入方法,需要注意的是,只能通过commit提交改变状态。在组件template中也要做出相应的修改

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

//component/Count.vue

//...
opbutton: {
template: `
<div>
<input type="button" value="+" @click="$store.commit('add')">
<input type="button" value="-" @click="$store.commit('mul')">
</div>
`
}

//store/store.js

//...
const mutations = {
add(state) {
state.count++
},
mul(state) {
state.count--
}
}

const VuexStore = new Vuex.Store({
state,
mutations
})

再次强调,只能通过commit提交改变state状态。

这样,就可以得到一个简单的计数应用。

使用getters过滤状态值

如果需要在获取数据之前对其进行处理,那么就在组件中添加计算属性,获取状态的变化。在这之前,需要再store容器中定义getters的方法。

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
//store/store.js
const getters = {
count(state){
return state.count += 100
}
}

const VuexStore = new Vuex.Store({
state,
mutations,
getters
})

//component/Count.vue
components: {
result: {
template: `
<div>
{{ count }}
</div>
`,
computed: {
count(){
return this.$store.state.count
}
}
}
}

为了简化在组件中的计算属性代码,引入mapGetters,简写这个节点与store容器同名的方法。

1
2
3
4
5
6
7
8
//component/Count.vue
import { mapGetters } from 'vuex'
result: {
//...
computed: {
...mapGetters(['count'])
}
}

使用State状态值

如果希望在点击发生后不只是加1,而是加1乘以3,那么在组件的计算属性中写入该方法。

1
2
3
4
5
6
7
8
9
//component/Count.vue
result: {
//...
computed: {
count(){
return this.$store.state.count * 3
}
}
}

与mapGetters类似,可以使用mapState辅助函数简写这一段代码

1
2
3
computed: mapState({
count: state => state.count * 3
})

mutations提交载荷

当需要给mutations里的方法传递另外的参数时,比如每次都需要加9,则代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//store/store.js
const mutations = {
add(state,n) {
state.count += n
}
}

//component/Count.vue
opbutton: {
template: `
<div>
<input type="button" value="+" @click="$store.commit('add',9)">
</div>
`
}

这种对mutations传入额外参数的方法称为提交载荷。在大部分情况下,载荷应该是一个对象。以上的代码修改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//store/store.js
const mutations = {
add(state,obj) {
state.count += obj.num
}
}
//component/Count.vue
opbutton: {
template: `
<div>
<input type="button" value="+" @click="$store.commit('add',{num:9})">
</div>
`
}

在组件点击事件中可以引入mapMutations简化。

1
2
3
4
5
6
7
8
9
10
11
//component/Count.vue
opbutton: {
template: `
<div>
<input type="button" value="+" @click="add({num:9})">
</div>
`,
methods: mapMutations({
'add': 'add'
})
}

actions

actions类似于mutations,区别在于:

  • actions能使用mutations中的方法
  • actions是异步,mutations是同步

证明actions能使用mutation中的方法

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
//store/store.js
const actions = {
addActions(context){
context.commit('add')
},
mulActions(context){
context.commit('mul')
}
}

const VuexStore = new Vuex.Store({
state,
mutations,
getters,
actions
})

//component/Count.vue
import { mapActions } from 'vuex'
opbutton: {
template: `
<div>
<input type="button" value="+" @click="addAction">
<input type="button" value="-" @click="mulAction">
</div>
`,
methods: {
...mapActions['addAction','mulAction']
}
}

证明actions是异步执行

修改部分以上的代码:

1
2
3
4
5
6
7
8
9
10
//store/store.js
const actions = {
addActions(context){
setTimeout( () => { context.commit('add') },3000)
console.log('我执行了')
},
mulActions(context){
context.commit('mul')
}
}

当点击”+”时可以发现,在actions提交之前,控制台上已经有了内容。

Module

如果应用复杂度比较高,此时就需要用模块划分状态管理,每个模块拥有自己的state、action、mutation、getter,甚至是嵌套的子模块。

1
2
3
4
5
6
7
8
9
10
11
12
//store/store.js
const moduleA = {
state,
actions,
mutations,
getters
}
var VuexStore = new Vuex.Store({
modules: {
a: moduleA
}
})

在模板或者计算属性中使用:

1
2
3
4
5
6
7
8
//component/Count.vue
<div>{{ $store.state.a.count }}</div>

computed: {
count(){
return this.$store.state.a.count
}
}