一.vuex是什么
官方解释:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。讲真的,这个解释,大部分人都不懂,换个通俗易懂的说法:Vuex用来存储数据的状态,即数据的变化。当vue应用程序的组件结构复杂程度提高时,采用传统的方法通信将变的繁琐而且不易维护,vuex正是为了解决这个问题而出现的。下面是vuex实现数据流的示意图:
接下来,我们一步步来理解其中专业名词的意义。
二.vuex核心概念及应用
解释这些概念之前,请先添加vuex到项目中:使用命令 vue add vuex(本文使用的是vue-cli@3.1.0)。
1.Store
每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state) 。
上述添加vuex的过程会在项目src目录下生成一个名字为store的文件夹,该文件夹下有一个名字为index.js的文件,它就是一个store,代码如下:
1 | import Vue from 'vue' |
2.State
Vuex 使用单一状态树——是的,用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源 (SSOT)”而存在。单一状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。
改写上面的store中state,没错,就是index.js文件,如下:
1 | state: { name: 'Robin', age: 27} |
在组件中我们使用$store.state.name来引用name属性,因为store中数据状态是响应式的,所以我们使用computed:
1 | <template> |
3.Getter
有这样一个场景:我们需要从 store 中的 state 中派生出一些状态,例如对列表进行过滤并计数:
1 | computed: { |
如果有多个组件需要用到此属性,我们要么复制这个函数,或者抽取到一个共享函数然后在多处导入它——无论哪种方式,都会写很多重复的代码。
Vuex 允许我们在 store 中定义“getter”(可以理解为 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的源被存储下来,且只有当它的源值发生了改变才会被重新计算。改写上面的index.js:
1 | import Vue from 'vue' |
在组件中引用:this.$store.getters.getTasks
。
4.Mutation
提交mutation是改变state中数据状态的唯一方法。Vuex中的mutation非常类似于事件:每个mutation都有一个字符串的事件类型(type)和一个回调函数(handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受state作为第一个参数,payload作为第二个参数(额外的参数):
1 | state: { |
在组件中使用的$store.commit方法:
1 | this.$store.commit('afterBirthday' , 1) |
注:mutation 里面的事件,必须是同步函数,同步函数,同步函数
5.Action
Action类似于mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
先定义一个action:1
2
3
4
5
6
7
8
9
10
11
12
13state: {
name: 'Robin', age: 27,
},
mutations: {
afterBirthday(state,adds) {
state.age += adds
}
},
actions: {
increment(context) {
context.commit("afterBirthday")
}
}
increment函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。
我们可以在store内部执行异步操作,改写上面的increment:
1 | increment(context) { |
在组件中调用action的方式为$store.dispatch:
1 | this.$store.dispatch('increment') |
6.modules
由于使用单一的状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂,store对象就会变得非常臃肿;为了解决这个问题,Vuex允许我们将store分隔成模块(module),每个模块拥有自己的state,mutation,action,getter,甚至是嵌套子模块。新建一个包含moduleA和moudleB的store,如下
1 | import Vue from 'vue' |
有以下几点需要说明:
1.在模块中,state是被限制到模块的命名空间下,需要命名空间才能访问,比如需要拿到A中的taskList:this.$store.state.A.taskList;
2.actions和mutations, getters没有被限制,在默认情况下(namespaced:false),它们是注册到全局命名空间下的,所谓的注册到全局命名空间下,其实就是我们访问它们的方式和原来没有module 的时候是一样的,例如,要访问getTasks:this.$store.getters[‘getTasks’];
3.当在module中设置namespaced:true时,actions 和mutations, getters就有域的限制,不能直接通过原来的方式访问,同样,要访问getTasks: this.$store.getters[‘A/getTasks’],其中’A’表示getTasks在‘A’模块之下。