【Vue】系列三 - Vuex


一、Vuex简介

作用:在Vue中实现集中式状态(数据)管理的一个Vue插件,对Vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。

什么时候用Vuex?

  • 多个组件依赖于同一状态
  • 来自不同组件的行为需要变更同一状态

总结一句话:多个组件需要共享数据时需要用Vuex。

Vuex工作原理:

二、搭建Vuex环境

安装:npm install vuex@3(如果不指定版本,就会默认安装最新版本vuex@4,而vuex@4对应的是Vue3,vuex@3对应的是Vue2)

2.1. 创建Store

创建Store文件:src/store/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
// 引入Vue核心库
import Vue from 'vue'
// 引入Vuex
import Vuex from 'vuex'
// 应用Vuex插件
Vue.use(Vuex)

// 准备actions对象:响应组件中用户的动作
const actions = {};
// 准备mutations对象:修改state中的数据
const mutations = {};
// 准备getters对象:用于将state中数据进行加工(类似于Vue的computed)
const getters = {}
// 准备state对象:保存具体的数据(类似于Vue的data)
const state = {}

// 创建并暴露store
export default new Vuex.Store({
actions,
mutations,
getters,
state
})

2.2. 绑定Store

main.js中创建vm时引入store配置项

1
2
3
4
5
6
7
import store from './store'

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

2.3. 基本使用

定义组件并在组件中使用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
<template>
<div>
<!-- 获取state中的数据 -->
<h2>求和:{{ $store.state.sum }}</h2>
<!-- 获取getters中的数据 -->
<h2>求和放到100倍:{{ $store.getters.bigSum }}</h2>
<button @click="increment"></button>
</div>
</template>

<script>
export default {
name: 'Counter',
data() {
return {
n: 1
}
},
methods: {
increment() {
// 发送事件
this.$store.dispatch('increment', this.n);
}
}
}
</script>

store/index.js中定义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
const state = {
sum:0
}

// 加工数据
const getters = {
// 参数state:全局共享数据对象
bigSum(state) {
return state.num * 100
}
}

// 接收action的commit
const mutations = {
// 接收两个参数:
// 第一个参数(state):就是全局共享数据对象
// 第二个参数(value):dispatch发送的值
INCREMENT(state, value) {
state.sum += value
}
};

// 响应组件中的dispatch动作(业务逻辑建议放在actions中)
const actions = {
// 接收两个参数:
// 第一个参数(context):是一个包含commit、dispatch、getters、state的对象
// 第二个参数(value):dispatch发送的值
increment(context, value) {
// 建议mutations中的type是大写
context.commit('INCREMENT', value)
}
};

2.4. 高级使用

如果在Vuex的state中有很多属性都需要在组件中使用,那么就会出现大量的$store.state.xxx代码,再加上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>
<h2>姓名:{{ name }}</h2>
<h2>性别:{{ sex }}</h2>
<h2>年龄:{{ age }}</h2>
</div>
</template>

<script>
export default {
name: 'Counter',
data() {
return {
n: 1
}
},
computed: {
name() {
return this.$store.state.name
},
sex() {
return this.$store.state.sex
},
age() {
return this.$store.getters.age
}
}
}
</script>

上面代码虽然规范了,但在计算属性中还是出现了重复的this.$store.state/getters.***。其实Vuex提供了一系列map函数,可以映射Store中的属性或函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
import {mapState, mapGetters} from 'vuex'

computed: {
// mapState返回的是一个对象(从store中的state读取数据),所以需要解构
// 传入对象:key是组件内映射使用的属性名,value是state中的属性名
...mapState({myName: 'name', sex: 'sex'})
// 传入数组:如果key和value是一样的,可以使用数组形式,但请不要使用ES6的对象简写语法
...mapState(['name', 'sex'])

// mapGetters使用方式和mapState一样,只不过mapGetters时从store中的getters读取数据
...mapGetters({myAge: 'age'})
...mapGetters(['age'])
}

mapState:用于帮助我们映射state中的数据为计算属性

mapGetters:用于帮助我们映射getters中的数据为计算属性

mapActions:用于帮助我们生成与actions对话的方法,即包含$store.dispatch(xxx)的函数

mapMutations:用于帮助我们生成与mutations对话的方法,即包含$store.commit(xxx)的函数

2.5. 模块化

实际开发中,我们会按照功能对Store配置项进行拆分,否则会太臃肿,并且不利于团队开发。

模块化和命名空间能够让代码更好维护,让多种数据分类更加明确。

store/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
35
// 引入Vue核心库
import Vue from 'vue'
// 引入Vuex
import Vuex from 'vuex'
// 应用Vuex插件
Vue.use(Vuex)

// 用户信息
const profileOptions = {
namespaced: true, // 开启命名空间
actions: {...},
mutations: {...},
getters: {...},
state: {
name: '',
sex: ''
}
}

// 设备信息
const deviceOptions = {
namespaced: true,
actions: {...},
mutations: {...},
getters: {...},
state: {...}
}

// 使用modules
export default new Vuex.Store({
modules: {
profile: profileOptions,
deviceInfo : deviceOptions
}
})

如果使用modules配置了Store的模块,一定要在配置项中加上namespaced: true,否则在使用map函数时,会出现找不到模块而报错。

开启命名空间后,组件中使用store:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// state
// 方式一:直接读取
this.$store.state.profile.name
// 方式二:mapState
...mapState('profile', ['name', 'age', 'sex'])

// getters
// 方式一:直接读取
this.$store.getters['profile/GETAGE']
// 方式二:mapGetters
...mapGetters('profile', ['age'])

// dispatch
// 方式一:直接读取
this.$store.dispatch('profile/increment', this.n)
// 方式二:mapActions
...mapActions('profile', {increment: 'increment'})

// commit
// 方式一:直接读取
this.$store.commit('profile/INCREMENT', this.n)
// 方式二:mapMutations
...mapMutations('profile', {increment: 'increment'})