目录
1、Vuex 是个啥?
2、单页面的状态管理
3、Vuex 的安装及使用
4、通过mutation改变state状态
5、Vuex 的几个核心概念
6、项目结构
7、Vuex 的优点
官方解释:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。
是不是不太好理解,那状态管理到底是什么?其实可以简单的将其看成把需要多个组件共享的变量全部存储在一个对象里面,然后将这个对象放在顶层的 Vue 实例中,让其他组件可以使用。一般在大型项目中,比如用户登陆状态、商品收藏、购物车中的的商品等,都是可以放在一个统一的地方,对其进行保存和管理,而且还是响应式的。
单个组件中进行状态管理是一件非常简单的事情,我们先从一个简单的 Vue 计数应用开始:
new Vue({
data () { //state:驱动应用的数据源
return {
count: 0 //视为要管理的状态
}
},
template: ` //view:以声明的方式将state映射到视图
{{ count }}
`,
methods: { //actions:响应在view上的用户行为导致的状态变化
increment () {
this.count++
}
}
})
以下是一个表示 "单向数据流" 理念的简单示意,像上面这种在单个组件中进行状态管理是很容易的(count相当于该组件的状态),再进一步说,在父子组件中进行状态管理也可以通过父子组件传值来实现共享状态,但是当我们的应用遇到多个组件共享状态时,虽然有办法实现共享状态(比如事件总线),但是单向数据流的简洁性很容易被破坏,比如:(1)多个视图依赖于同一状态、(2)来自不同视图的行为需要变更同一状态。像上面这两种情况就并不能很好的管理共享状态。因此我们把组件的共享状态抽取出来,以一个全局单例模式(像是仓库)管理,在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为,这就需要用到我们的 Vuex。
3.1、工程化的方式进行开发安装 Vuex:
npm install vuex --save
3.2、Vuex 的配置:
>第一步:导入 vuex 对象,并调用 Vue.use(Vuex)
>第二步:创建 vuex 实例,并且进行vuex的相关配置
>第三步:在 Vue实例中挂载创建的 vuex 实例
3.3、Vuex 具体配置代码:项目的 src 文件夹下新建 store/index.js文件,在里面对vuex进行相关的配置,代码如下:
import Vue from 'vue'
import Vuex from 'vuex'
//1.安装插件
Vue.use(Vuex) //底层是会执行Vuex插件的install方法
//2.创建store对象
const store = new Vuex.Store({
state:{}, //state中存放需要共享的状态,在其他多个界面可以通过$store.state拿到这个对象
mutations:{}, //不推荐通过$store.state.count++这种方式改变state状态,而是应该是显式地提交 (commit) mutation
actions:{},
getters:{}, //类似单个组件中的计算属性computed(){}
modules:{}
})
//导出router
export default store
3.4、配置完 vuex 之后需要到 main.js 文件对 store 进行引入和挂载:
import Vue from 'vue'
import App from './App.vue'
import store from './store' //这里会默认导入store/index.js
Vue.config.productionTip = false
new Vue({
render: h => h(App),
store //内部原理:Vue.prototype.$store = store
}).$mount('#app')
完成上述操作后,Vuex 就配置完成了,可以在将项目中需要共享的状态放到 vuex 中,便于状态管理。
Vuex 不推荐通过 $store.state.count++ 这种方式改变 state 状态,而是应该是 显式地提交 (commit) mutation,这样 Devtools 可以记录每次状态的改变,方便开发人员查看 state 的修改过程。下面看一个简单的案例:
(1)提取出一个公共的 store 对象,用于保存在多个组件中共享的状态(2)将 store 对象挂载到 new Vue 对象中,这样可以保证在所有的组件中都可以使用到(3)在其他组件中使用 store 对象中保存的状态即可。
>通过 this.$store.state.属性 的方式来访问状态
>通过 this.$store.commit('mutation中方法')来修改状态
import Vuex from ' vuex'
import vue from' vue'
Vue.use(Vuex)
const store = newVuex.store({
state:{
count:0
},
mutations:{ //mutations中存放改变state中状态的方法
increment(state){
state.count++
},
decrement(state){
state.count--
}
}
})
export default store
{{count}}
5.1、State:单一状态树
Vuex 中的 state 就是管理状态信息的,而且在其他组件中可以通过 this.$store.state.属性 的方式来访问 state 中的状态。Vuex 在 state 方面还提出了一个概念:单一状态树(Single Source of Truth),也可以翻译成单一数据源。
是不是不太好理解,简单的说如果你的状态信息是保存到多个 Store 对象中的,那么之后的管理和维护等等都会变得特别困难,所以 Vuex 也使用了单一状态树来管理应用层级的全部状态,单一状态树能够让我们以最直接的方式找到某个状态的片段,而且在之后的维护和调试过程中也是非常方便的。State 单一状态树:说白了就是无论你有多少数据,都推荐你使用一个 store 来保存。
5.2、Getters:类似单个组件中的计算属性 computed(){}
有时候我们需要从 store 中获取一些 state 变异后的状态,比如 state 对象中有很多学生对象,学生对象有年龄姓名 id 等属性,然后你使用的时候要从中提取出年龄大于 20 的学生信息,这就是从 store 中获取一些 state 变异后的状态。
Vuex 中的 getters 就是为满足上面这种场景所存在的,下面举个例子看一下它的基本使用:
getters:{
powerCounter(state){ //默认传入一个state参数
return state.counter * state.counter
}
}
//在store中写完上述代码后,到需要获取数据的组件中对其引用:$store.getters.powerCounter,不用加()
5.3、Mutation:状态更新、Vuex的store状态更新的唯一方式:提交Mutation
Mutation 主要包括两部分:(1)字符串的事件类型(type)、(2)一个回调函数(handler),该回调函数的第一个参数就是 state。在通过 mutation 更新数据的时候,有可能我们希望携带一些额外的参数,参数被称为是 mutation 的载荷(Payload)
举个例子,比如说:按钮点击之后提交一个学生的相关信息,简要代码如下:
//vuex的store中的mutations代码如下:
mutations:{
addStu(state,stu){ //第二个是载荷
state.student.push(stu); //state中有student数组
}
}
//组件中的简要代码,点击按钮,添加学生信息
addStudent(){
const stu = {id:114,name:'erha',age:18}; //创建一个新学生对象
this.$store.commit('addStu',stu); //这里的stu就是载荷
//Vue还提供了另外一种风格,它是一个包含type属性的对象
this.$store.commit({
type:'addStu',
stu:stu
});
}
5.4、Action:Action类似于Mutation,但是是用来代替Mutation进行异步操作的
通常情况下,Vuex 要求我们 Mutation 中的方法必须是同步方法,主要的原因是当我们使用 devtools 时,devtools 可以帮助我们捕捉 mutation 的快照,但是如果是异步操作,那么 devtools 将不能很好的追踪这个操作什么时候会被完成。但是某些情况,我们确实希望在 Vuex 中进行一些异步操作,比如网络请求必然是异步的,那就需要用 Action 代替 Mutation 进行异步操作。
举个例子,比如说:按钮点击之后的一秒钟(异步)提交一个学生的相关信息,简要代码如下:
mutations:{
addStu(state,stu){ //第二个是载荷
setTimeout(() => { //异步操作,实际上可以修改state,但是devtools捕捉不到
state.student.push(stu); //state中有student数组
},1000)
}
}
//如果是异步操作,则需要对其进行如下改进
mutations:{
addStu(state,stu){ //第二个是载荷
state.student.push(stu); //state中有student数组
}
}
actions:{
acAddStu(context,payload){ //context:上下文,可以将其视为store
setTimeout(() => {
context.commit('addStu');
console.log(payload);
},1000)
}
}
//组件中的简要代码,点击按钮,添加学生信息
addStudent(){
this.$store.dispatch('acAddStu','payload载荷')
}
在现实开发的过程中可能会遇到这种问题,异步操作actions中的修改操作执行成功了之后,要告诉外面组件的点击回调函数,现在修改成功了,可以做其他的事情了,一般认为actions中的方法发送了commit方法给mutations的时候,就认为是修改成功了,这样的话只需要在外部的组件中的点击回调中的dispatch方法后面写一个回调函数就行了。所以上面例子可以做一下改写:
actions:{
nameUpdateInfo(context,payload){ //context:上下文,可以将其视为store
return new Promise((resolve,reject)=>{ //返回一个Promise,进而可以调用Promise的then方法
setTime(() => {
context.commit('mutations改name的方法'); //看起来环节多余,但是必须这样做
console(payload); //这里就是打印:'携带的信息'
resolve('修改成功了');
},1000)
})
}
}
//组件中的简要代码
this.$store.dispatch('actions中的方法名','携带的信息').then(res => {
console.log('里面已经完成了')
console.log(res); //打印的'修改成功了'
})
5.5、Module:模块
前面介绍了 State 单一状态树,它意味着很多状态都会交给 Vuex 来管理,当应用变得非常复杂时,store 对象就有可能变得相当臃肿,为了解决这个问题,Vuex 允许我们将 store 分割成模块(Module),而每个模块拥有自己的 state、mutations、actions、getters 等。下面来具体看一下 moudles 的基本使用:
const moduleA = {
state:{},
mutations:{},
getters:{},
actions:{}
}
const moduleB = {
state:{},
mutations:{},
getters:{},
actions:{}
}
const store = new Vuex.Strore({
state:{},
mutations:{},
getters:{},
actions:{},
modules:{
a:moduleA,
B:moduleB
}
})
可以在各个组件中通过 this.$store.state.a.属性 拿到 a 模块中的状态。
如果上述的 a 模块中有 mutations 的时候,怎么使用?其实和直接定义在 store 对象中的 mutations 的使用方法一样,注意的是:模块中的 mutations 和主 store 中的 mutations 的名字不要重复,当你点击回调发送 commit 方法的时候,它会先到模块中去找,没有的话再去主 store 的 mutations 中去找方法,进行 commit。
如果上述 a 模块中有 getters 的时候,怎么使用? getters 也是不关心你定义再模块里还是定义在 store 里,用的时候都是直接到组件中写上 {{$store.getters.方法}},直接调用 getters 就可以了。
如果上述 a 模块中有 actions 的时候,怎么使用?actions 有一个默认的参数 context,和其他的方法不同,mutations 和 getters 都是默认的 state 参数,actions 默认的 context 参数就说明 actions 中写的异步操作只能提交到自身模块的 mutations 中。
Vuex 并不限制你的代码结构。但是,它规定了一些需要遵守的规则:(1)应用层级的状态应该集中到单个 store 对象中。(2)提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。(3)异步逻辑都应该封装到 action 里面。
实际开发中不会将 vuex 的所有代码都写到一个 store/index.js 文件下,这样太不便于维护了,Vue官网推荐我们:如果项目中的 store 文件太大,只需将 action、mutation 和 getter 分割到单独的文件。
├── index.html
├── main.js
├── api
│ └── ... # 抽取出API请求
├── components
│ ├── App.vue
│ └── ...
└── store
├── index.js # 我们组装模块并导出 store 的地方
├── actions.js # 根级别的 action
├── mutations.js # 根级别的 mutation
└── modules
├── cart.js # 购物车模块
└── products.js # 产品模块
6.1 store/index.js 文件
import mutations from './mutations'
import actions from './actions'
import getters from './getters'
import moduleA from './modules/moduleA'
const state = { //这里全部都写state相关的东西
counter:1000;
student:{
{id:1,name:'erha',age'182},
{id:2,name:'erha',ag3'18'},
{id:3,name:'erha',age'18'}
}
info:{name:'jinmap',age:18}
}
const store = new Vuex.Store({ //Vuex中有一个Store属性,真正用的是store类,所以起名字叫store
state:state, //这里的state就是引用上面的satate对象,ES6可以只写一个state,
mutations:mutations, //可只写一个mutations
actions:actions, //可只写一个actions
getters:getters, //可只写一个getters
modules:{
a:moduleA
}
})
6.2 store/mutations.js 文件
export default {
//这里写mutations相关的代码
}
6.3 store/actions.js 文件
export default {
//这里写actions相关的代码
}
6.4 store/getters.js 文件
export default {
//这里写getters相关的代码
}
6.5 store/modules/moduleA.js 文件
export default {
//这里写moduleA相关的代码
}
Vuex 全局状态管理,也叫全局数据管理,它的优点有哪些?