在使用vue作为框架的前端项目开发中,我们经常会碰到Vuex,那么Vuex到底是什么东西呢?
根据官方文档给出的解释是:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
简单来说,Vuex就是一个状态管理的库,或者说是一个用来存放组件公共数据的仓库。
要了解为什么要使用Vuex,那么我们就需要先来了解vue项目中组件之间的传参方式
父组件向子组件传参:
// 父组件传递参数
// 子组件接收参数
{{ fatherData }}
子组件向父组件传参:
// 父组件
{{sonData}}
// 子组件
组件之间跳转的传参方法(示例由组件A跳转到组件B):
// A组件
// B组件
{{ bData }}
从上面三种组件之间传参的方式我们可以看出,组件与组件之间传递参数实现数据共享,那么组件之间需要有一定联系,要么是父子组件,要么是有跳转的关系,才能实现数据的共享。可是在完全没有联系的组件之间,他们如何实现数据的共享呢?
要想实现完全没有联系的组件之间数据共享,我们需要先思考一个问题,什么样的数据,需要同时在几个没有联系的组件中去使用呢?
在项目开发中,我们经常会碰到一种情景,比如说一个系统,在用户登录之后,后端会将该用户的一些数据返回给前端,如用户名,token等,而这些用户数据往往不止在一个页面中使用,可能涉及到多个相互没有联系的组件,那么在这种时候,我们就需要使用到Vuex。
对于一个插件的使用,我们首先需要看对应的官方文档:Vuex 是什么? | Vuex (vuejs.org)。根据文档,接下来我们来一起理解Vuex。
在Vuex中有五个属性,分别为state,getters,mutations,actions,modules,下面我们将从这五个属性来学习Vuex。
从上面的概念我们知道,Vuex是用来存放一些共享的数据,实现状态管理的一个插件,那么,我们的这些数据要放在那里呢?
在项目目录下,我们可以找到一个名为store的文件夹,文件夹中存放一个index.js的文件
我们之后关于Vuex的一些操作,基本都会在这个文件夹下的文件中实现。
首先我们来看一下index.js文件中都有些什么
// 导入vue
import Vue from 'vue'
// 导入Vuex
import Vuex from 'vuex'
// 注册Vuex组件
Vue.use(Vuex)
// 导出Vuex.Store
export default new Vuex.Store({state: {},getters: {},mutations: {},actions: {},modules: {}
})
我们可以看到,index.js中的内容基本和其他的插件的使用方式一样,我们主要看Vuex.Store中的内容,我们可以看到前面讲到的五个属性就是在Vuex.Store中,而我们的数据,就会放在state中。那么,怎么使用这个数据呢,我们使用文档中举的例子来示例。
首先,我们需要在state中加入我们要在其他组件中使用的数据
// store/index.js
export default new Vuex.Store({state: {// 添加一个变量numnum: 0},
})
在store中添加了数据之后,我们需要到对应的组件中使用
组件A中使用
{{$store.state.num}}
组件B中使用
{{num}}
在这里我举例了两种组件调用state中数据的方法,文档中还提到了其他的方法,可以自行查看。
到这里我们发现,我们已经实现了将数据存放在一个地方,可以供多个不相关的组件一起调用,那么好像数据共享就完成了一部分,但是我们也知道,前端的数据往往不是单纯地进行展示,而是需要进行操作,那么,对于Vuex中的数据,我们要怎么进行操作呢?
按照前端开发的思路,原则上只要我们获取到数据,好像就可以对数据进行操作了,我们既然通过store.state.num可以获取到num,那么对store.state.num可以获取到num,那么对store.state.num可以获取到num,那么对store.state.num进行操作就可以了,按这个思路,我们可以看看是否成功?
通过试验,我们发现,页面展示的数据确实达到了想要的效果,但是通过Vue开发者工具,我们发现,Vuex的state一直都没有改变
很显然,我们不能直接通过$store.state.num来改变数据,那么我们到底需要怎么样改变数据呢?
通过看Vuex的文档,我们可以在mutations属性介绍中发现这样一句话:更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。那么根据这句话,我们就可以知道,想要修改num,必须提交mutation,根据文档的使用方法,我们更改一下代码:
// store/index.js
export default new Vuex.Store({state: {// 添加一个变量numnum: 0},mutations: {addNum(state) {state.num++;}},
})
结果也符合预期:
那么到这里,数据的展示和操作好像都已经完成了,可是我们发现,还有三个属性都没有用到,那么这三个属性又有什么样的用处呢?
我们知道,在实际的项目开发中,我们会碰到很多异步操作,比较常见的就用axios请求,定时器等,那么我们对Vuex中的数据进行异步操作时是不是还是使用mutations呢?
很显然,大家看目录到这里,就知道对Vuex中的数据进行异步操作是要用到actions的,但是我们在学习mutations的时候有了解到更改 Vuex 的 store 中的状态的唯一方法是提交 mutation,那么actions是如何提交mutation的呢?
export default new Vuex.Store({state: {// 添加一个变量numnum: 0},mutations: {addNum(state) {state.num++;}},actions: {addNum(context) {setTimeout(() => {context.commit('addNum')},1000)}},
})
我们可以看到在action中,有一个addNum方法接收了一个context参数,然后再方法中有一个定时器,定时器中通过conntext.commit调用了addNUm,看到.commit我们就会看到调用mutations时用的就是**this.store.commit(′addNum′),∗∗那是不是达标,action接收的那个context参数就是this.store.commit(‘addNum’),** 那是不是达标,action接收的那个context参数就是this.store.commit(′addNum′),∗∗那是不是达标,action接收的那个context参数就是this.store呢?我们使用console.log分别输出this.$store和context
在控制台中,我们可以看到,这两者有类似的属性,但是又不完全相同,但是,两者都有着同样的方法,那么就有可能是,context与store存在着某些联系,可以通过context.commit提交mutations。根据文档给出的解释是,Action函数接收了一个与store实例具有相同方法和属性的context对象,使我们可以通过context.commit来提交mutations。
modules的中文意思是模块,那么见名思意,我们可以大致理解为modules属性就是用来存放模块的。那么,在存放模块之前,就意味着我们需要现有模块。
在Vuex中,是允许我们将store分割成模块,每个模块拥有自己的state,mutations,action,getters甚至是modules,那么我们想象一下,为什么我们需要将store分割成模块呢?
我们知道,组件化开发,可以将根据功能划分组件,然后再讲组件整合在一起,这样既有利于我们分工,也增加了代码的复用性和便于后期的维护。而将store显然也是出于这样的考虑,我们知道,在一个大型的项目,需要进行状态管理的数据往往不是一两个变量,而是一个很复杂庞大的数据,如果我们将这些数据全部写到index.js文件中,那么整个文件就会显得冗余,而是可读性很低,那么这时候就需要我们将这些数据分割成不同的模块。
// store/index.js
export default new Vuex.Store({state: {},mutations: {},actions: {},modules: {user,menu,tab}
})
例如这样,我们就可以根据我们的需要,将user和menu的数据分割成一个模块,这样store/index.js这个文件就会显得很精简,而且也便于我们的更改。
getters这个属性主要用于对state中的数据进行一些数据处理,在某种程度上来说,比较像组件中的computed。这里的数据处理和上面的mutations的数据操作并不是同一个意思。
getters的数据数据处理是指将state处理之后在组件中展示,而mutations则是指在触发某些事件之后对数据进行对应的操作。
举个例子,我们现在state中的num是0,我们知道在点击事件提交mutations之后num会加1,但是我们现在希望在增加多一个变量的前提下,组件A展示的是1,组件B展示的是0,那么在这个时候,我们就需要用到getters对state的num进行数据处理
export default new Vuex.Store({state: {// 添加一个变量numnum: 0},getters: {getAddNum(state) {return state.num+1} },mutations: {addNum(state) {state.num++;}},actions: {addNum(context) {console.log("context",context)setTimeout(() => {context.commit('addNum')},1000)}},
})
组件A:
{{$store.getters.getAddNum}}
这样,我们就可以达成我们的目的:
Vuex的使用还有很多需要注意的地方,本文只是简单的理解Vuex的几个属性的使用方法。
需要注意的地方是:由于Vuex的版本升级,很多地方进行了修正(详情可以查看官方文档),本文的代码是基于"vuex": "^3.4.0"版本的,如果按本文的代码练习出现错误,请寻找对应的Vuex版本代码进行学习。