随着项目越来越复杂,我们可能向store中添加的状态越来越多,这不便于我们对数据的管理,同时,既然vue是模块化开发,那么我们能否将Vuex中的状态也进行模块化管理呢?
Vuex——Module
Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a
store.state.b
值得注意的是,这里两份模块的state写法state: () => ({ ... }) ,这里通过函数来返回了一个state对象。如果我们不这样写,而是使用一个纯对象来声明模块的状态,那么这个状态对象就会通过引用被共享,导致状态动向被修改时store或模块间数据互相污染的问题。实际上,这和Vue组件内的date类似,它也是使用了一个函数。
但是,模块化store对象之后,如果将所有的内容都写在一个store.js中,就显得过于臃肿,我们难以快速定位数据,这时候,我们还可以按照模块将store分成不同的文件。
import defaultState from "./state/defaultState";
import mutationsRoot from "./mutations/mutations.root";
import actionsRoot from "./actions/actions.root";
import gettersRoot from "./getters/getters.root";
import stateModuleA from "./state/state.moduleA";
import mutationsModuleA from "./mutations/mutations.moduleA";
import gettersModuleA from "./getters/getters.moduleA";
import actionsModuleA from "./actions/actions.moduleA";
export default () => {
return new Vuex.Store({
state: () => defaultState(),
mutations: mutationsRoot,
actions: actionsRoot,
getters: gettersRoot,
modules:{
a: {
namespaced: true,
state: stateModuleA(),
mutations: mutationsModuleA,
getters: gettersModuleA,
actions: actionsModuleA
}
}
})
}
所以现在的目录结构为:
使用模块化Vuex
以下就是store中的a模块:
modules:{
a: {
namespaced: true,
state: () => {
return {
numA: 10,
numB: 20
}
},
mutations: {
updateNum (state, data) {
state.numA = data.numA
state.numB = data.numB
}
},
getters: {
sum(state, getters, rootState){
return state.numA + state.numB
}
},
actions: {
updateNumsAsync (store, data) {
setTimeout(() => {
store.commit('updateNum', data.nums)
}, data.time)
}
}
}
}
在app中使用模块中的状态:
<template>
<div>
<div>{{numA}} {{numB}}</div>
<div>{{sum}}</div>
</div>
</template>
<script>
import {
mapState,
mapMutations,
mapActions,
mapGetters
} from 'vuex'
export default {
name: 'app',
methods:{
...mapMutations(["a/updateNums"]),
...mapActions(["a/updateNumsAsync"])
},
mounted() {
console.log(this.numA)
this['a/updateNumsAsync']({
nums: {
numA: 50,
numB: 40
},
time: 3000
})
},
computed:{
...mapState({
numA: state => state.a.numA,
numB: state => state.a.numB
}),
...mapGetters({
sum: 'a/sum'
})
}
}
</script>
注意:
- 默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。如果希望你的模块具有更高的封装度和复用性,你可以通过添加
namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。也就是说,我们在a模块中定义的updateNum,sum,updateNumsAsync其实是注册在全局命名空间的,但是由于我们设置了namespaced: true ,所以注册这些函数时,将其名称由函数名=>模块名称/函数名,即a/updateNum等。 - 在使用mapGetters,mapMutations,mapActions来简化代码风格时,我们的函数名发生了改变,所以不能直接使用updateNum,而是应该使用a/updateNum;但是,如果我们使用mapState来简化代码,a模块中的state不能通过a/状态名称(即a/numA)来获取,而是通过
numA: state => state.a.numA 来获得。如果不使用mapState,我们可以通过this.$store.state.a.numA 来获取状态。 - 在使用a模块下的函数时,比如
...mapGetters({sum: 'a/sum'}) ,其实我们也可以这样...mapGetters([ 'a/sum' ]) ,但是,这样写并不便于在模板中使用getter函数。如果在js中,我们可以使用this['a/sum'](); 来调用getter函数,但是如果我们在模板中,<div>{{a/sum}}</div> ,这种写法是无法达到我们的期望的。所以,在使用store模块中的函数时,我们应当先为其设置一个别名,然后通过别名使用它。
|