什么是Vuex?
抛开官网上晦涩的解释,个人理解是Vuex是一个前端的状态”数据库”,它可以让各组件都能共享这个单例,触发行为更新视图。这张图可以看出这个关系:
Vuex的核心是 store 。在其中包含了应用中的大部分状态。具有以下两个特点:
- 状态储存响应式。状态修改时响应组件相应组件也能得到更新。
- 不能直接修改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.vue1
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.js1
2
3
4
5
6
7
8
9
10
11
12
13
14import 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 |
|
再次强调,只能通过commit
提交改变state
状态。
这样,就可以得到一个简单的计数应用。
使用getters过滤状态值
如果需要在获取数据之前对其进行处理,那么就在组件中添加计算属性,获取状态的变化。在这之前,需要再store
容器中定义getters
的方法。
1 | //store/store.js |
为了简化在组件中的计算属性代码,引入mapGetters,简写这个节点与store
容器同名的方法。
1 | //component/Count.vue |
使用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
3computed: mapState({
count: state => state.count * 3
})
mutations提交载荷
当需要给mutations里的方法传递另外的参数时,比如每次都需要加9,则代码为:
1 | //store/store.js |
这种对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 | //component/Count.vue |
actions
actions类似于mutations,区别在于:
- actions能使用mutations中的方法
- actions是异步,mutations是同步
证明actions能使用mutation中的方法
1 | //store/store.js |
证明actions是异步执行
修改部分以上的代码:
1 | //store/store.js |
当点击”+”时可以发现,在actions提交之前,控制台上已经有了内容。
Module
如果应用复杂度比较高,此时就需要用模块划分状态管理,每个模块拥有自己的state、action、mutation、getter,甚至是嵌套的子模块。
1 | //store/store.js |
在模板或者计算属性中使用: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
}
}