前言
上一篇文章我们简单介绍了一下 Vuex 的简单使用,但随之也会产生一个问题。由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。尤其是在多人开发中会显得特别不便利。
介绍
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割。深入学习可能还是要去看官方文档
语法:
在Vuex的配置中通过modules配置不同的模块,modules中模块的配置语法是模块名:{state,getters,mutations,action,modules}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| import Vue from 'vue' import Vuex from 'vuex' import commend from './commend'
Vue.use(Vuex)
export default new Vuex.Store({ state: { name: 'root' }, mutations: { }, actions: { }, modules: { commend: { state: { name: '小明', age: 18 }, getters: { }, mutation: { }, actions: { }, modules: { } } } })
|
虽然在我们在实例化vuex.Store时创建了不同的模块,但是最终生成的store对象所有数据(包含子模块的数据)依然存储在同一个state getters mutations actions中。
命名空间
vuex中的modules支持命名空间,设置了命名空间的模块与没有设置命名空间的模块存在一定的区别。
语法:在需要开启命名空间的module模块中添加一个配置选项,namespaced:true
module state
语法:存放在module中的state,是否开启命名空间使用方式都是一样的。都是通过store.state.
模块名.state属性名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| let store = new Vuex.Store({ state: { name: 'root' }, modules: { commend: { state: { name: '小王' } } a1: { namespaced: true, state: { name: '老李头', age: 56 } } } })
|
实例:辅助函数mapState获取指定模块的属性,必须使用 "属性名:函数的形式"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <template> <div id="app"> <button @click="showStore">show</button> <p>{{name}}</p> <p>{{commendName}}</p> <p>{{a1Name}}</p> </div> </template>
<script> import {mapState} from 'vuex'
export default { name: 'App', computed: { ...mapState(['name']), ...mapState({ commendName: state => state.commend.name, a1Name: ({a1}) => a1.name }) }, methods: { showStore() { console.log(this.$store) } } } </script>
|
module getters
语法:所有module中的getter是直接存储在store.getters中的,依然通过store.getters.属性名访问
。
所以在开发中如果你的模块如果没有开启命名空间,一定要保证未命名模块与未命名模块之间/未命名模块与根getters之间不能有同名getters属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| export default new Vuex.Store({ state: { name: 'root' }, getters: { test(state) { return state.name + '!!' } reverseName(state) { return state.name.split('').reverse().join('') } },
modules: { a1: { state: { name: '老李头', age: 56 }, getters: { reverseName(state) { return state.name.split('').reverse().join('') } } } } })
|
注意:当开启了命名空间后module,getters 获取方式发生改变
变为 –>store.getters['模块名/getter属性名']
所以开启命名空间的getter可以与其他模块根store中的getter同名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| export default new Vuex.Store({ state: { name: 'root' }, getters: { test(state) { return state.name + '!!' } reverseName(state) { return state.name.split('').reverse().join('') } },
modules: { a1: { namespaced: true, state: { name: '老李头', age: 56 }, getters: { reverseName(state) { return state.name.split('').reverse().join('') } } } } })
|
实例:辅助函数mapGetter 获取module的getter方法没有变化的
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import { mapGetters} from 'vuex'
export default { name: 'App', computed: { ...mapGetters(['reverseName']), ...mapGetters({a1ReverseName: 'a1/reverseName'}) }, methods: { showStore() { console.log(this.$store) } } }
|
注意:模块中getters函数包含四个参数
参数一 state ——– 当前模块的state
参数二 getters ——– 当前模块的getters(如果当前模块没有开启命名空间 该参数的值等于参数四)
参数三 rootState ——– 根state store的state
参数四 rootGetter ——– 根getters store的getters
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| export default new Vuex.Store({ state: { name: 'root' }, getters: { reverseName(state) { return state.name + '!!' } }, modules: { a1: { namespaced: true, state: { name: '老李头', age: 56 }, getters: { reverseName(state,getters, rootState, rootGetters) { console.log('state',state) console.log('getters',getters) console.log('rootState',rootState) console.log('rootGetters',rootGetters) return state.name.split('').reverse().join('') } } } } })
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| export default new Vuex.Store({ state: { name: 'root' }, getters: { rootReverseName(state) { return state.name + '!!' } }, mutations: { }, actions: { }, modules: { a1: { state: { name: '老李头', age: 56 }, getters: { reverseName(state,getters, rootState, rootGetters) { console.log('state',state) console.log('getters',getters) console.log('rootState',rootState) console.log('rootGetters',rootGetters) return state.name.split('').reverse().join('') } } } } })
|
module mutation 和 action
语法:所有mutation和action也是直接存储在store中的,依然通过store.dispatch('mutation名') store.dispatch('action名')
触发的 。
在开发中如果你的模块如果没有开启命名空间,mutation与action 在模块与模块之间 或模块与根store之间存在同名mutation / action 是不会造成命名冲突不会报错的,但是如果commit / dispatch 这些同名 mutation / action时他们将都会执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| export default new Vuex.Store({ state: { name: 'root' }, getters: { rootReverseName(state) { return state.name + '!!' } }, mutations: { myMutation() { console.log('根mutation的 myMutation方法被触发了') } }, actions: { myAction() { console.log('根action的 myAction方法被触发了') } }, modules: { a1: { mutations: { myMutation() { console.log('模块 a1 mutation 的 myMutation方法被触发了') } }, actions: { myAction() { console.log('模块 a1 action的 myAction方法被触发了') } } } } })
|
注意:当开启了命名空间后module,mutation与action方法名会发生改变
变为 –> '模块名/mutation名' '模块名/action名'
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| export default new Vuex.Store({ state: { name: 'root' }, getters: { rootReverseName(state) { return state.name + '!!' } }, mutations: { myMutation() { console.log('根mutation的 myMutation方法被触发了') } }, actions: { myAction() { console.log('根action的 myAction方法被触发了') } }, modules: { a1: { namespaced: true, state: { name: '老李头', age: 56 }, mutations: { myMutation() { console.log('模块 a1 mutation 的 myMutation方法被触发了') } }, actions: { myAction() { console.log('模块 a1 action的 myAction方法被触发了') } } } } })
|
注意: 模块中mutation函数无论是否开启具名空间依然只包含两个参数
参数一: 当前模块的局部state
参数二: 载荷
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| export default new Vuex.Store({ state: { name: 'root' }, getters: { rootReverseName(state) { return state.name + '!!' } }, mutations: { myMutation() { console.log('根mutation的 myMutation方法被触发了') } }, modules: { a1: { namespaced: true, state: { name: '老李头', age: 56 }, mutations: { myMutation(state, payload) { console.log('模块 a1 mutation 的 myMutation方法被触发了') } } } } })
|
注意: 模块中action函数无论是否开启具名空间依然只包含两个参数
参数一: context 与root state比较多了两个属性
参数二: 载荷
1 2 3 4 5 6
| commit // 用来提交mutation的方法 dispatch // 用来分发action的方法 getters // 命名空间开启局部getters 没有开启命名空间 全局getters rootGetters // 全局getter rootState // 全局state state // 当前模块的statestate
|
注意:命名空间开启的模块
action内部 context
.commit 提交mutation时dispatch分发action时会自动添加命名空间前缀
,从而实现只提交当前模块mutation/action
的效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| export default new Vuex.Store({ state: { name: 'root' }, getters: { rootReverseName(state) { return state.name + '!!' } }, mutations: { myMutation() { console.log('根mutation的 myMutation方法被触发了') } }, actions: { myAction() { console.log('根action的 myAction方法被触发了') } }, modules: { a1: { namespaced: true, state: { name: '老李头', age: 56 }, mutations: { myMutation() { console.log(arguments,'模块 a1 mutation 的 myMutation方法被触发了') } }, actions: { myAction(context) { context.commit('myMutation') context.dispatch('a1/myAction') console.log(arguments,'模块 a1 action的 myAction方法被触发了') } } } } })
|
注意: 如果你想要在开启命名空间模块的
action中使用
context提交/分发全局mutation/action请在
commit dispatch方法传入
第三个参数{root:true}代表调用全局
1 2 3
| dispatch('someOtherAction', null, { root: true })
commit('someMutation', null, { root: true })
|
例子如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| export default new Vuex.Store({ state: { name: 'root' }, getters: { rootReverseName(state) { return state.name + '!!' } }, mutations: { myMutation() { console.log('根mutation的 myMutation方法被触发了') } }, actions: { myAction() { console.log('根action的 myAction方法被触发了') } }, modules: { a1: { namespaced: true, state: { name: '老李头', age: 56 }, mutations: { myMutation() { console.log(arguments,'模块 a1 mutation 的 myMutation方法被触发了') } }, actions: { myAction(context) { context.commit('myMutation',null,{ root: true }) context.dispatch('myAction',null,{ root: true }) console.log(arguments,'模块 a1 action的 myAction方法被触发了') } } } } })
|
CodeSandbox
CodeSandbox在线代码演示
注意 如遇到CodeSandBox打开失败,请尝试按下图操作,然后分窗口就能一边看代码一边看效果啦
