在使用vue的时候,如果需要对整个页面的数据进行修改,往往要从触发事件的子组件,用emit向上传递到值所在的父组件,如果组件繁多的话,中间的组件只是起到传递方法的作用,代码会显得很琐碎,所以对于这种情况,我们可以考虑将需要进行更新的数据放在一个全局的地方,将对数据的更新操作也放在全局,在组件中只要去调用这个全局的内容就可以了,Vuex实现了这一功能。
根据官方文档说的:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
在我的理解看来,Vuex实际上就是将数据及对数据的更新放在一个全局位置供所有组件去调用,这样就不用对那些过多组件的应用写上一大段琐碎的代码了。
Vuex有几个核心的概念,state,getters,mutation,actions,module,看一个简单的架构
直接创建对象
const store = new Vuex.Store({
module: {
// ...
},
state: {
// ...
},
getters: {
// ...
},
mutations: {
// ...
},
actions: {
// ...
},
})
模块化开发
// store对象所在的文件
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
modules: {
// ...
}
});
export default store;
// 要导入的模块所在的文件
const state={
// ...
}
const getters={
// ...
}
const mutations={
// ...
}
const actions={
// ...
}
export default { state , getter , mutations , actions }
下面说说在模块化开发中如何去使用Vuex
使用vue-cli创建项目后,下载vuex的依赖包,在src文件夹下新建store文件夹来存放数据,在store下建一个module和一个index.js
要在模块化开发中使用Vuex,如果子组件要使用Vuex中的数据,那根组件必须导入store对象
新建一个count组件,在app.vue中引入该组件并导入store对象
App.vue
<template>
<div id="app">
<Count></Count>
</div>
</template>
<script>
import store from '../../store'
import Count from 'components/count.vue'
export default {
name: 'app',
components: {
Count
},
store
}
</script>
<style>
</style>
count.vue
<template>
<div>
<span>{{count}}</span>
<button>add</button>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
在store下的module下建一个count.js放置要用到的数据,在index.js中配置对应的内容
index.js
import Vue from 'vue';
import Vuex from 'vuex';
import count from './modules/count';
Vue.use(Vuex);
const store = new Vuex.Store({ // 注意这里的Store的s要大写,不然会报错
modules: {
count
}
});
export default store;
count.js
const state = {
}
const getters = {
}
const mutations = {
}
const actions = {
}
export default { state, getter, mutations, actions }
State
state,状态,是存放数据的地方,我们将数据放在state
因为Vuex的状态存储是响应式的,所以我们要在computed中获取这个数据,当数据更新时也能对应更新
在count.js的state中写入数据
const state = {
count: 0
}
count.vue中在计算属性中导入该值
// count.vue
export default {
computed:{
count(){
return this.$store.state.count.count;
}
}
}
这样就可以将state中的数据在count.vue中使用,担如果我们需要state的多个数据,这样写显得代码有点冗余,所以我们可以借用mapState辅助函数来导入数据
export default {
computed:mapState({
count:state=>state.count.count
})
}
因为mapState返回的是一个对象,要在computed中同时写入其他值,可以使用扩展运算符来将mapState返回的对象内容放到computed中
export default {
computed:{
...mapState({
count:state=>state.count.count
})
}
}
getters
在获取count时,我们可以发现是state.count.count而不是state.count,state.count实际上是调用了conut.js返回的对象,如果我们要更方便地获取数据或者对其做一些过滤处理,可以使用getter来处理
count.js
const getters = {
getCount(state) {
return `${state.count}次`
}
}
count.vue
export default {
computed:{
count(){
return this.$store.getters.getCount
}
}
}
当然,对于getters也有一个辅助函数用于解决导入较多数据时的冗余问题,mapGetters
import { mapGetters } from "vuex"
export default {
computed:{
...mapGetters({
count:"getCount"
})
}
}
mutation
mutation用于改变改变状态,简单说就是改变state中的属性的值。在Vuex中只能在mutation中改变值
count.js
const mutations = {
add(state) {
state.count++;
}
}
count.vue
<template>
<div>
<span>{{count}}</span>
<button @click="$store.commit('add')">add</button>
</div>
</template>
若要在方法中传入一个参数(在文档中写为载荷),直接跟在参数state后即可
count.js
const mutations = {
add(state, n) {
state.count += n;
}
}
count.vue
<template>
<div>
<span>{{count}}</span>
<button @click="$store.commit('add',2)">add</button>
</div>
</template>
要注意的是,mutation只能是同步函数,因为devtools要在执行mutation时记录提交前后的状态,如果是异步函数,我们无法知道状态到底什么时候会发生改变,也就无法记录状态了,要使数据修改在异步的情况下进行,要借助于actions来触发mutation里面的数据修改
actions
上面说过了,要异步修改数据,只能在actions中实现,这里写一个简单的异步修改
count.js
const actions = {
add(context) {
// context是一个与store对象有相同方法和属性的对象
setTimeout(() => {
context.commit('add', 2);
}, 500);
}
}
这里真正修改数据的还是在mutation中,只是在action中通过使用异步提交的形式,达到异步修改数据的结果
count.vue (actions使用store.dispatch来触发)
<template>
<div>
<span>{{count}}</span>
<button @click="$store.dispatch('add')">add</button>
</div>
</template>
要对每个方法都使用dispatch分发过于麻烦,Vuex也提供了辅助函数mapActions来解决这个问题
count.vue
<template>
<div>
<span>{{count}}</span>
<button @click="add">add</button>
</div>
</template>
<script>
import { mapGetters ,mapActions } from "vuex"
export default {
computed:{
...mapGetters({
count:"getCount"
})
},
methods:{
...mapActions([
'add'
])
}
}
</script>
<style>
</style>
在count.js中,可以看到每次调用mutations的时候都要加个context.xx很麻烦,所以可以使用参数解构来简化代码
const actions = {
add({ commit }) {
setTimeout(() => {
commit('add', 2);
}, 500);
}
}