
前言
在学习Vuex之前,进行兄弟组件的传值使用最多的是使用第三个组件(EventBus)或者父组件,这种方式虽然对小项目比较友好,但是对于企业项目或规模比较大的项目就比较麻烦。而在Vue官方文档,提供了一种状态管理的方式Vuex,组成了Vue生态的重要部分。Vuex可以将复杂的大型项目的数据管理得清晰条理性,让数据流在每一个组件和模块变得可控、可追溯,这样使得它成为项目开发中的大杀器。
1 什么是Vuex?
在官方文档里的陈述是:Vuex
是一个专为Vue.js
应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
个人理解:Vuex
就是仓库管理员,负责对每一类别的商品(data
)进行统一管理,实现分门别类、快速查找和获取等功能,真正做到像商品一样管理数据。哪个地区需要相应的商品便会配送过去,对应的仓库的商品也会对应进行改变。Vuex
这个仓库可以实现对全局状态数据的管理,可以方便快捷的实现组件之间数据的共享。
那么,使用`Vuex`进行数据状态的统一管理有什么好处呢?
- 能够在
Vuex
中进行集中管理共享的数据,便于开发和后续维护 - 能够高效地实现组件之间的数据共享和通信,提高开发效率
- 存储在
Vuex
中的数据是响应式的,能够实时保持数据与页面的同步
一直说`Vuex`是进行数据状态管理的一种模式,那么什么样的数据适合存储在`Vuex`中呢?
就实际开发情况而言,一般的小项目可以不使用Vuex
,也能做到很好的数据管理。对于较为大型的企业项目可以采用Vuex
,对组件之间共享的全局数据进行管理,而对于组件中的私有数据,依旧保存在组件自身的data
中进行处理。
2 什么是“状态管理模式”?
在官方文档提到了“状态管理模式”这个词语,那么到底什么是“状态管理模式”呢?如果你想知道,就跟着我的脚步,带你一起研究。
简要版的状态管理模型包括:state
、view
以及actions
等三个部分。
state
相当于组件中的data
,负责用于存放全局的数据(即仓库),驱动应用的数据源。view
是数据待渲染展示的视图页面,可以将state
数据映射到页面上。actions
就如同字面意义进行行动、变化,对在view
上做出的数据改变做出响应。
如下图所示,是一个简单的“单向数据流”模型的示意图,看似是一个稳定的三角形结构,但其实它的数据流一点也不稳定。当多个组件同时共享这个“单向数据流”模型的数据时候,我们需要考虑的是多个视图同时依赖于同一个数据状态,以及来自多个不同视图的行为需要变更同一数据状态。

我们知道在之前的组件传参是通过层级嵌套进行数据传递,这就导致和千层饼一样,一层套一层,老套娃了,而对于兄弟组件的传值却无能为力。而面对数据状态的更新和同步时,采取的方法往往是通过父子组件直接引用或者事件驱动的形式。这种方式操作繁杂、难以维护。
而Vuex
的实现原理就是将组件的共享的状态数据抽取放在仓库中,以一个全局的“状态管理模式”触发数据在视图上进行渲染。将全局的组件构建成的一个巨大的视图树,而数据就相当于树干,能够把数据流传递到每一个视图子叶上,任何组件都能获取状态或者触发行为!
我们可以在下面的Vuex
的构成关系图中可以看出:
State
是作为数据管理的仓库、数据存储的容器。Getter
相当于组件中的Computed
属性,getter
的返回值会根据依赖进行缓存,当依赖值发生变化时会重新计算进行使用。Mutation
是用于更新store
中的state
数据状态的唯一方法,其相当于组件中的methods
,但是它不能进行异步方法的操作(定时器、axios异步请求等)。Actions
其实和Mutation
的作用类似,只不过Actions
是Vuex
中用于专门处理异步方法的,通过操作Mutation
进行更新数值状态。Module
是用于对模块进行分割管理的,当数据比较庞大时,在同一个文件中不便于管理,此时可以按照功能需求进行模块封装。Plugins
是对状态进行快照、记录和追溯等功能的插件,可以实现对数据状态的便捷管理。- 辅助函数
mapState
、mapGetters
、mapActions
、mapMutations
等是便于开发者在组件的vm
中处理store
数据,便于管理操作。当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性。

个人理解:
3如何使用Vuex?
最简单的Vuex示例
下面代码是一个最简单`Vuex`示例demo,可以看到每一个项目的`Vuex`应用都是一个`store`,而`store`中的数据状态不能直接进行操作,而必须通过`mutations`中方法进行同步更新数据。也就是,在开发中不能更改`state`中`count`的数值,而必须通过`mutations`中的`increment()`方法进行自增操作。
store.js代码:
import Vue from 'vue';
import Vuex form 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
//state相当于数据的仓库,类似于组件中的data
state: {
count: 0,
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
// 定义"getter"(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
getters:{
//筛选出所有todo.done为true的值
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
},
//mutations相当于组件中的methods,用于处理同步事件
mutations: {
//自增操作
increment(state) {
state.count++
},
//自减操作--这里使用了两个参数,state是表示仓库的数据,payload表示要操作的载荷对象
decrease(state,payload){
state.count -= payload.decNum//payload.decNum表示每次递减的数据
}
},
// 处理异步事件
actions: {
increment(context){
context.commit("add");
},
/*
increment({commit}){//此处使用了对象解构
commit("add");
}
*/
incrementAsync({commit}){
setTimeout(()=>{
commit("add");
},1000)
}
}
})
复制代码
test.js代码:
<template>
<div>
<h1>数据:{{$store.state.count}}</h1>
<h1>采用计算属性:{{count}}</h1>
<button @click="addCount()">数据+1</button>
</div>
</template>
<script>
export default {
computed:{
count(){
//此处的store已经进行了全局挂载
return this.$store.state.count;
},
//通过store.getters对象调用
doneTodos () {
return this.$store.getters.doneTodos
}
},
methods:{
addCount(){
//调用仓库中的increment方法进行count自增操作
this.$store.commit("increment");
},
// 进行异步操作
addAsync(){
this.$store.dispatch("incrementAsync");
},
}
}
</script>
复制代码
仓库中的count
数据状态除了可以在标签中使用$store.state.count
进行获取,也可以使用computed
进行获取,当然其实一样的。我们看到这样每次通过computed
获取store
中对应的数据是不是很复杂,而Vuex
提供了相应的数据映射mapState
在相应的组件批量导入数据,极大简化了我们的操作。同样的,我们也能使用辅助函数mapGetters
进行映射计算,辅助函数mapMutations
可以将组件中的methods
映射为store.commit
,辅助函数mapActions
将组件中异步函数映射为store.dispatch
,达到简化代码的目的。
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex';
export default {
/*
computed: {{
count: state => state.count,
countAlias: 'count', // 别名 `count` 等价于 state => state.count
})}
*/
//更加简化的版本
computed:{
...mapState(["count"]),//映射this.count为store.state.count
...mapGetters(["doneTodosCount"]),//使用对象展开方式将getters混入computed中
...mapMutations([
increament,//注意此处必须是处理同步函数
])
},
methods:{
...mapActions(["incrementAsync"])
}
}
复制代码
上面代码中store
在Vue
中进行了全局挂载,这样在项目就可以进行全局操作使用Vuex
中的数据,可以将数据流注入所有的Vue
组件进行使用。
main.js代码:
import Vue from "vue";
import App from "./App.vue";
import store from "./store";
Vue.config.productionTip = false;
new Vue({
router,
store,
render: (h) => h(App),
}).$mount("#app");
复制代码
结语
我愿意用最通俗易懂的语言去表达我对Vuex的理解,如果在理解有偏差还请大家指出,谢谢。
参考文档
- https://juejin.cn/post/6844903558496665607
- https://juejin.cn/post/6844903784708046855
- https://vuex.vuejs.org/zh/guide/actions.html