1.创建项目并安装vuex

1
2
npm create project_name
npm i vuex --save # 也可在创建project_name时进行选择安装

安装后需要进行配置配置:

  • 新建src/store/index.js文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import Vue from 'vue'
    import Vuex from 'vuex'
    //安装使用并导出
    Vue.use(Vuex)

    export default new Vuex.Store({
    state: {
    },
    mutations: {
    },
    actions: {
    },
    modules: {
    }
    })
  • 在main.js中进行挂载

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

    Vue.config.productionTip = false

    new Vue({
    store, //此处作用
    render: h => h(App)
    }).$mount('#app')

    作用:在其他Vue组件中,我们就可以通过this.$store的方式,获取到这个store对象了

    挂载到Vue实例中 相当于执行 Vue.prototype.$store = store ,将store对象放置在new Vue对象中,这样可以保证在所有的组件中都可以使用到。

2.状态管理的流程

image-20210114101225342

当我们去修改Vuex 中的 State的时候,我们不应该直接去修改 State ,而是通过 Actions、Mutations 进而来修改State。

Devtools:它是Vue开发的一个浏览器插件,通过Mutations,它可以记录我们每一次修改State。

Actions:如果我们请求是异步操作的话,我们会通过Actions,将异步转化为同步,因为Devtools通过Mutations来监听我们对 State 的修改只能是同步操作。如果请求是同步的话,我们可以绕过Actions。

3.state

state里存放需要共享的变量,每个vue组件都可以通过访问$store.satate来访问共享数据

在index.js中的state定义共享变量

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

Vue.use(Vuex)

export default new Vuex.Store({
state: {
counter:100,
},
mutations: {
},
actions: {
},
modules: {
}
})

在所有的vue组件中都能使用这个共享变量counter

helloworld.vue中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template>
<div class="hello">
<h1>{{ $store.state.counter }}</h1>
</div>
</template>

<script>

export default {
name: 'HelloWorld',
}
</script>


app.vue中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<div id="app">
<h1>{{ $store.state.counter }}</h1>
<HelloWorld/>
</div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
name: 'App',
components: {
HelloWorld
}
}
</script>

4.getters

有时候我们并不需要访问的state中的所有数据,或者希望它进行加工后得到的数据,就可以使用getter,它的功能类似于computed

<h1>{{ this.$store.getters.powerCounter }}</h1>

index.js

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
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
state: {
counter:100,
},
getters: {
powerCounter(state){
//当操作数据是数组时,也可通过filter等函数进行过滤操作
return state.counter * state.counter
},
fun1: function (state,getters){
//默认第一个参数是state,第二个是getters
return 0
},
fun1: function (state,geters){
//除了返回值,还能返回函数,接收带参数的调用
return function(param1){
return 1 //使用param1进行state或getter的一些操作最终返回一个值
}
}
},
mutations: {
},
actions: {

},
modules: {
}
})

5.mutation

state中的共享变量的更改必须通过mutation中的定义methods(理解为事件)进行,这样DevTools才能跟踪修改,便于开发调试。

而调用mutation中的方法是通过this.$store.commit(“method_name”)进行的

index.js中

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
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
state: {
counter:100,
},
mutations: {
//含义:increment是事件类型名,后面跟一个回调函数
//只能通过this.$store.commit("method_name")
increment(state){
state.counter++
},
decrement(state){
state.counter--
},
//提交mutation时
decrementWithPayload(state,param){
//do things with param
},
//另一种提交风格
decrementInAnotherStyle(state,payload){
//do thing
// payload: {type:"decrement",param:param}
},
},
actions: {
},
modules: {
}
})

app.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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<template>
<div id="app">
<h1>{{ counter }}</h1>
<button @click="increment()">+</button>
<button @click="decrement()">-</button>
<HelloWorld/>
</div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
name: 'App',
computed:{
counter:function (){
return this.$store.state.counter
}
},
components: {
HelloWorld
},
methods:{
increment(){
this.$store.commit("increment")
},
decrement(){
this.$store.commit("decrement")
},
//提交mutation事件时,也可带上参数,多个参数时需要封装称对象传入
decrementWithPayload(param){
this.$store.commit("decrement",param)
},
//另一种提交风格,此时mutation中第二个参数接收的是一个对象
decrementInAnotherStyle(param){
this.$store.commit({
type:"decrement",
param, //es6语法
})
},
}
}
</script>

mutation响应更新,在旧版本中使用的是watcher,也就是它只会对state中原有的数据进行监听和响应,新加入的对象属性是不会响应更新的,需要使用Vue.set(state.obj,”objAttr”,attrVal)或Vue.delete(state.obj,”objAttr”)等方式,而新版的vue中默认都会进行响应更新。

在开发过程中,因为commit时提交的是事件名,字符串形式容易出现拼写错误,所以会使用常量来代替事件名

  • 新建一个js文件存放常量名,并导出

  • 在index.js中导入

  • 使用常量定义mutation中的事件

    1
    2
    3
    4
    5
    mutations: {
    [THIS_IS_A_CONSTANT](state){
    state.counter++
    },
    }
  • 在需要commit的地方导入常量,然后使用 this.$store.commit(THIS_IS_A_CONSTANT)

6.action

当需要在mutation中进行异步操作时,state中的数据是不会实时动态刷新的。此时需要把异步操作放在action中进行,然后再action中调用mutation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
export default {
name: 'App',
methods:{
doSomeAsync(){
this.$store.dispatch("actionName")
this.$store.dispatch("actionNameWithPayload",payload)
this.$store.dispatch("actionUsePromise",payload).then(function(res){
//do something after finish async
}
})
},
}
}
</script>
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
mutations: {
mutationName(state){
state.counter++
},
//同样可以带参数
mutationWithPayload(state){
state.counter++
},
},
actions: {
actionName(context){
//setTimeout模拟异步操作
setTimeout(function(){
context.commit("mutationName")
//do something after finish async
},3000)
},
actionNameWithPayload(context,payload){
//setTimeout模拟异步操作
//can use payload
setTimeout(function(){
context.commit("mutationWithPayload",payload)
//do something after finish async
},3000)
},
//使用promise进行改写,把异步操作完成后的do someting放在调用action处的then中
actionUsePromise(context,payload){
return new Promise(function (resolve,reject){
setTimeout(function(){
context.commit("mutationWithPayload",payload)
resolve(payload)
},3000)
})
}
},

当需要不同的action之间需按一定顺序执行时,就可以封装为promise的形式

还可以把组件中的方法映射到action中,详细见官网mapActions使用,类似的还有mapGetters

7.modules

当store中的内容很多时,不便于管理维护,可以划分子模块,每个模块是一个独立的store,它们包含再根store中

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
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const moduleA={
state: {
valA:100, //访问方式:$store.state.moduleA.valA 因为最终该module会被放入根store的state中
},
getters: { //先在根store中查找,找不到才会去各个module查找
//getter的使用和根store的一样,不同的是,它可以由第三个参数rootState,用于访问根state的信息
getValA(state,getters,rootState){
return state.valA* state.valA
}
},
mutations: { //与根mutation使用一样,先在根store中查找,找不到才会去各个module查找
mutationName(state){
state.counter++
},
},
actions: { //action中的context是本模块的context,commit时也只能提交到本module的mutation中
actionName(context){
//该context除了含有本module的数据外还有rootState和rootGetters用于访问根store
setTimeout(function(){
context.commit("mutationName")
},3000)
},
},
}
export default new Vuex.Store({
state: {
},
getters: {
},
mutations: {
},
actions: {
},
modules: {
moduleA:moduleA
}
})

8.结构划分

可以把index.js中的代码进行抽取划分到多个文件中,便于开发维护。

把文件抽离为如下结构

image-20210114140116618

index.js就变成了如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import Vue from 'vue'
import Vuex from 'vuex'
import moduleA from "./modules/moduleA";
import getters from "./getters";
import actions from "./actions";
import mutations from "./mutations";
Vue.use(Vuex)

const state = {
counter:1,
}
export default new Vuex.Store({
state,
getters,
mutations,
actions,
modules: {
moduleA:moduleA
}
})