Vuex是由Vue官方开发的一个专为Vue.js开发的状态管理库。Vuex可以集中存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
Vuex基于“单向数据流”理念:
Vuex的工作模式如下:
Vuex应用的核心就是Store(仓库)。Store基本上就是一个容器,它包含着应用中大部分的状态(State)。
使用Vuex也可以实现任意组件间通信。但是官方在文档中提到,如果要构建一个简单的网页应用,使用Vuex可能是繁琐冗余的。
如果应用够简单,最好不要使用Vuex。一个简单的Store模式就足够了。
如果需要构建一个中大型单页应用,就需要考虑如何更好地在组件外部管理状态,Vuex将会成为自然而然的选择。
安装 Vuex
在Node.js环境下,可以运行下方命令安装Vuex:
1npm i vuex
但是需要注意的是,目前不指定Vuex版本的情况下,安装的是vuex@4,而vuex@4适用于Vue3,适用于Vue2的Vuex版本是vuex@3。
1# 安装适用于 Vue2 的 Vuex 3
2npm i vuex@3
安装好后,就可以在项目中导入Vuex:
1import Vuex from 'vuex'
导入Vuex后,需要在Vue中开启Vuex插件:
1Vue.use(Vuex)
配置 Vuex
Vuex的基本配置如下(Vuex配置文件可以是项目根目录下的vuex/store.js或store/index.js):
1import Vue from 'vue'
2import Vuex from 'vuex' // 引入 Vuex
3
4Vue.use(Vuex) // 使用 Vuex
5
6// Actions 用于响应组件中的动作
7const actions = {
8 /* ... */
9}
10
11// Mutations 用于操作数据
12const mutations = {
13 /* ... */
14}
15
16// State 用于存储数据
17const state = {
18 /* ... */
19}
20
21// Getters 用于将 State 中的数据进行加工
22const getters = {
23 /* ... */
24}
25
26// 创建并导出 Store
27export default new Vuex.Store({
28 actions,
29 mutations,
30 state,
31 getters,
32})
创建好Store之后,需要将其导入到main.js中,并配置在Vue实例上:
1import Vue from 'vue'
2import store from './store'
3
4/* ... */
5
6new Vue({
7 /* ... */
8 store,
9 /* ... */
10}).$mount('#app')
配置好后,当前Vue实例,以及Vue实例下的所有组件,都能通过this.$store访问Store的API。
使用 Store
Store中,常用的API如下:
$store.dispatch():将数据分发给对应的Action。$store.commit():将数据(载荷)提交给对应的Mutation.
Store中,常用的数据对象如下:
$store.state:访问State中的状态数据(可以认为是一个全局的data)。$store.getters:对State中的状态数据进行相应处理,并获取对应的处理结果(可以认为是一个全局的,没有Setter的computed)。
dispatch
$store.dispatch(actionName, data):
actionName:指定一个Action的名称,String类型。对应store配置中actions配置对象下的一个相同名称的方法actionName()。data:分发给actionName()的数据,任意类型。actionName()使用第2个形参value接收。
每一个Action,在actions中都有一个与之对应的方法。在组件实例中,使用$store.dispatch()来将数据分发给对应的Action方法处理。每个Action都有一个唯一的actionName。
通常,actionName是以小驼峰规则命名。
1this.$store.dispatch('demoAction', value)
demoAction对应的Action:
1const actions = {
2 /* ... */
3 demoAction(context, value) {
4 /* demoAction的处理逻辑... */
5 },
6 /* ... */
7}
context是一个上下文对象,每一个Action都会接收到这个参数。context封装了$store中的一些方法。通过context,可以将当前处理转发给其它Action,或是将处理提交到某个Mutation。
context中封装了如下方法和对象:
context.dispatch()context.commit()context.statecontext.getters
这些方法和对象的用法与$store中的相同。
context可以看作是一个小型的$store,它封装了$store中常用的方法和对象。使用者可以根据处理逻辑(上下文)调用context中的方法,所以才命名为context。在Action中,应该将
context.state视为只读的。因为Action的职责不是对状态进行修改,并且Vue.js devtools(Vue.js开发者工具)捕获不到Action对context.state的修改(尽管这些修改可能会生效),所以应该尽量避免在Action中对context.state的修改。
通常情况下,Action是用来对数据进行一些校验或简单的处理,然后将数据转派给下一个Action或提交给某个Mutation。
$store.dispatch()在调用时可以仅传递一个参数,也就是actionName。例如:
1this.$store.dispatch('plusOne')
commit
$store.commit(mutationName, payload):
mutationName:指定一个Mutation的名称(事件类型),String类型。对应store配置中mutations配置对象下的一个相同名称的方法mutationName()。payload:提交给mutationName()的数据(载荷),任意类型。mutationName()使用第2个形参value接收。
通常,mutationName是以全大写+下划线规则命名。
$store.commit()的用法和$store.dispatch()十分相似,不同的是$store.commit()是将载荷提交给某个Mutation。
1this.$store.commit('demoMutation', payload)
demoMutation对应的Mutation:
1const mutations = {
2 /* ... */
3 demoMutation(state, payload) {
4 /* demoMutation的处理逻辑... */
5 },
6 /* ... */
7}
state即对应Store配置中的state配置对象(相当于$store.state)。每一个Mutation都会接收到这个state对象。通过state对象,Mutation可以获取State中的状态数据,并对状态数据进行编辑。
与Action不同,Mutation通常是作为状态数据处理的角色存在。Mutation可以直接修改State中的数据;而Action通常不修改State中的数据,Action一般只是读取State中的数据。
Action不用作修改数据还有一个原因,如果在Action中修改State中的数据,Vue.js devtools并不能捕获到这些修改。即使在Action对State的修改能生效,也不建议在Action中对State进行修改,在Actions中,应该将State(
context.$state)视为只读的。
一般情况下(官方给的示意图中),Store对数据的处理流程是:$store.dispatch $\Rightarrow$ $store.commit。实际上,对一些简单的操作,不需要对传递的数据进行校验或处理的情况下,可以在组件实例中直接调用$store.commit将数据提交给Mutation处理。
$store.commit()在调用时可以仅传递一个参数,也就是mutationName。例如:
1this.$store.commit('plusOne')
state
在组件中读取State中的数据,可以通过this.$store.state.dataName的形式读取(dataName指要读取的数据)。需要注意的是,在组件实例中不要修改State中的数据。也就是说,在组件实例中应该将this.$store.state视为只读。
例如在Store的state配置项中定义一个状态数据:
1const state = {
2 siteTitle: 0, // 网站的标题
3}
在组件实例中获取state中的数据:
1this.$store.state.siteTitle
在组件实例中推荐使用computed来获取state中的数据:
1export default {
2 /* ... */
3 computed: {
4 siteTitle() {
5 return this.$store.state.siteTitle
6 },
7 },
8 /* ... */
9}
也可以通过computed的Setter来修改state中的数据:
1export default {
2 /* ... */
3 computed: {
4 /* ... */
5 siteTitle: {
6 get() {
7 return this.$store.state.siteTitle
8 },
9 set(payload) {
10 this.$store.commit('SET_SITE_TITLE', payload)
11 }
12 },
13 /* ... */
14 },
15 /* ... */
16}
1const mutations = {
2 /* ... */
3 SET_SITE_TITLE(state, payload) {
4 state.siteTitle = payload
5 }
6 /* ... */
7}
接着,就可以在组件实例中,像使用一般的数据一样,对siteTitle进行各种操作。
getters
$store.getters就像是一个全局的computed,可以在获取State中的数据之前,对数据进行一些处理。但与computed不同的是,getters只能用于获取,不能用于修改数据。
在Store的getters配置中,每个getter以函数的形式被定义,且每个getter接收一个state参数。这个state参数用于获取State中的数据。
每个getter都有一个唯一的名称,并且就像computed一样,在Vue实例中使用this.$store.getterName来获取相应的getter,getterName与getter定义时的函数名称相同。getter同样也是使用返回值来确定值。
1const getters = {
2 /* ... */
3 demoGetter(state) {
4 /* demoGetter 的处理逻辑... */
5 return /* demoGetter 的值 */
6 },
7 /* ... */
8}
例如要获取一个转为大写的siteTitle:
1const getters = {
2 siteTitleUpperCase(state) {
3 return state.siteTitle.toUpperCase()
4 },
5}
在组件实例中获取该值:
1this.$store.gettets.siteTitleUpperCase
同样可以使用computed来读取该值:
1siteTitleUpperCase() {
2 return this.$store.gettets.siteTitleUpperCase
3}
Vuex 映射
在组件实例中,使用Store中的state和getter时,配合computed可以使代码更加精简。而Vuex原型正好提供了一些辅助的API,可以在组件实例中快速地为state和getters中的数据创建对应的computed。
mapState()mapGetters()mapMutations()mapActions()
在使用这些辅助函数之前,需要先引入。这些辅助函数的引入方法都是相同的,下面进行例举了辅助函数的引入方式:
1import { mapState } from 'vuex' // 引入 mapState
1import { mapState, mapGetters } from 'vuex' // 引入 mapState 和 mapGetters
mapState
mapState()可以将State中的数据映射到组件实例中的computed中。
1computed: mapState({
2 computedAttr1: 'stateAttr1',
3 computedAttr2: 'stateAttr2',
4 computedAttr3: 'stateAttr3',
5 /* ... */
6}),
或者使用对象扩展运算符:
1computed: {
2 ...mapState({
3 computedAttr1: 'stateAttr1',
4 computedAttr2: 'stateAttr2',
5 computedAttr3: 'stateAttr3',
6 /* ... */
7 }),
8 /* ... */
9}
上方所示的两个mapState()相当于在computed中进行如下定义:
1computed: {
2 computedAttr1() {
3 return this.$store.state.stateAttr1
4 },
5 computedAttr2() {
6 return this.$store.state.stateAttr2
7 },
8 computedAttr3() {
9 return this.$store.state.stateAttr3
10 },
11 /* ... */
12}
也就是说,mapState()返回的是一个类似于computed配置对象的对象。这个对象中的每个属性都是函数类型,相当于一个个设置了Setter的computed属性。
使用mapState()生成的computed被称为vuex bindings(可以在Vue开发者工具中查看)。
mapState()可以传入两种类型的参数:
-
一种就是如上所示的
Object类型参数。传入的对象中,其每个属性的
key是作为实例中的computed属性名称,每个属性的value以String类型的形式指定了State中某个属性的名称。该方式通过传入对象中的每个属性的
value指定State中的属性,然后以每个属性的key作为它们的computed属性来生成vuex bindings。 -
另一种方式就是传入字符串数组类型的参数。
1mapState([ 2 'stateAttr1', 3 'stateAttr2', 4 'stateAttr3', 5 /* ... */ 6]),这种方式通过字符串数组指定一系列要作为
computed(vuex binding)属性使用的State属性。这种方式可以直接使用对应State属性的名称来使用这些vuex binding属性。上方
mapState()相当于在computed中配置了以下内容:1stateAttr1() { 2 return this.$store.state.stateAttr1 3}, 4stateAttr2() { 5 return this.$store.state.stateAttr2 6}, 7stateAttr3() { 8 return this.$store.state.stateAttr3 9},
mapGetters
mapGetters()可以将getters中的数据映射到组件实例中的computed中。生成的属性同样被称为vuex bindings。
mapGetters()的用法和mapState()几乎相同,它们都可以传入两种类型的参数,并且返回值也是一个属性都为函数类型的对象。
1mapGetters({
2 gettersAttr1: 'gettersAttr1',
3 gettersAttr2: 'gettersAttr2',
4 gettersAttr3: 'gettersAttr3',
5 /* ... */
6})
1mapGetters([
2 'gettersAttr1',
3 'gettersAttr2',
4 'gettersAttr3',
5 /* ... */
6])
上述两种使用方式,都是相当于在computed进行如下配置:
1computed: {
2 gettersAttr1() {
3 return this.$store.getters.gettersAttr1
4 },
5 gettersAttr2() {
6 return this.$store.getters.gettersAttr2
7 },
8 gettersAttr3() {
9 return this.$store.getters.gettersAttr3
10 },
11 /* ... */
12}
mapMutations
mapMutations()用于在methods中快速生成提交(commit)对应Mutations事件的方法。mapMutations()同样拥有两种形参类型。
mapMutations()作用于methods,但是用法基本上与mapState()和mapGetters()相同。并且mapMutations()传入的也是一个属性为function类型的对象。
1methods: mapMutations({
2 increment: 'INCREMENT',
3 decrement: 'DECREMENT',
4}),
相当于在methods中进行如下配置:
1methods: {
2 increment(value) {
3 this.$store.commit('INCREMENT', value)
4 },
5 decrement(value) {
6 this.$store.commit('DECREMENT', value)
7 },
8}
所以在调用或者绑定事件时,需要以increment()或increment(value)的形式。否则,以increment(如@click="increment")的形式绑定事件,传入的value参数就是当前绑定事件的对象(可能会造成数据错误)。
mapMutations()数组参数写法如下:
1methods: mapMutations([
2 'INCREMENT',
3 'DECREMENT',
4]),
相当于:
1methods: {
2 INCREMENT(value) {
3 this.$store.commit('INCREMENT', value)
4 },
5 DECREMENT(value) {
6 this.$store.commit('DECREMENT', value)
7 },
8}
mapActions
mapActions()用于在methods中快速生成分发(dispatch)对应Action的方法。
mapMutations()的用法基本上与mapMutations()相同。
1methods: mapActions({
2 increment: 'increment',
3 decrement:'decrement',
4}),
1methods: mapActions([
2 'increment',
3 'decrement',
4]),
上述两种方式相当于:
1methods: {
2 increment(value) {
3 this.$store.dispatch('increment', value)
4 },
5 decrement(value) {
6 this.$store.dispatch('decrement', value)
7 },
8}
同样使用increment()或increment(value)的形式调用或绑定事件。
评论