当项目非常大时,如果所有的状态都集中放到一个对象中,store 对象就有可能变得相当臃肿。
为了解决这个问题,Vuex允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割。
namespaced表示当前模块是否使用命名空间,如果使用的话,那么设置了namespaced属性的模块将和其它模块独立开来,调用时得指定命名空间后才可以访问得到
例如:
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>Document</title>
- <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
- <script src="./vuex.js"></script>
- </head>
- <body>
-
- <div id="app">
- <p>count:{{count}}</p>
- <p>Acount:{{Acount}}</p>
- <button @click="test1">测试1</button>
- <button @click="test2">测试2</button>
- </div>
- <script>
- const moduleA ={ //子仓库a
- state:{count:0},
- mutations:{Aincrement(state){state.count++}},
- actions:{Aincrement(context){context.commit('Aincrement')}}
- }
- const store = new Vuex.Store({ //创建Store实例
- modules:{A:moduleA},
- state:{count:1},
- mutations:{increment(state){state.count++}},
- actions:{increment(context){context.commit('increment')}}
- })
- new Vue({ //创建Vue实例
- el:"#app",
- store, //把实例化后的store作为new Vue的一个参数
- computed:{
- ...Vuex.mapState(['count']),
- ...Vuex.mapState({Acount:state=>state.A.count})
- },
- methods:{
- ...Vuex.mapActions(['increment','Aincrement']),
- test1(){
- this.increment();
- },
- test2(){
- this.Aincrement();
- }
- }
- })
- </script>
- </body>
- </html>
我们在根仓库定义了count状态,在子仓库A也定义了一个count,然后渲染如下:

点击测试1按钮将触发根仓库的increment这个action,点击按钮2将触发子仓库A的Aincrement这个action,分别给当前仓库的count递增1
像上面例子里区分的子module,它的mutations和actions都是和根仓库的等级是一样的,如果子仓库和根仓库的mutation或者action重名了,那么就会合并为一个数字,当触发时都会执行,例如:
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>Document</title>
- <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
- <script src="https://unpkg.com/vuex@3.1.0/dist/vuex.js"></script>
- </head>
- <body>
- <div id="app">
- <p>root.no:{{no}}</p>
- <p>Amodule.no:{{Ano}}</p>
- <button @click="test">测试1</button>
- </div>
- <script>
- const store = new Vuex.Store({
- state:{no:100},
- mutations:{
- increment(state,no){state.no+=no;}
- },
- modules:{
- A:{
- state:{no:50},
- mutations:{
- increment(state,no){state.no+=100;}
- }
- }
- }
- })
- var app = new Vue({
- store,
- computed:{
- ...Vuex.mapState({no:state=>state.no,Ano:state=>state.A.no})
- },
- methods:{
- ...Vuex.mapMutations(['increment']),
- test(){
- this.increment(10);
- }
- },
- el:'#app'
- })
- </script>
- </body>
- </html>
我们点击测试1按钮时将触发根仓库和子仓库A的increment这个mutation,此时页面会将两个对应的no都分别进行更新,这样是不符合逻辑的,最好每个仓库都互不干扰
writer by:大沙漠 QQ:22969969
我们可以给子仓库定义一个namespaced属性,值为true,表示开启命名空间,这样,各个仓库间的mutation、getter就不会有冲突了,例如:
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>Document</title>
- <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
- <script src="https://unpkg.com/vuex@3.1.0/dist/vuex.js"></script>
- </head>
- <body>
- <div id="app">
- <p>root.no:{{no}}</p>
- <p>Amodule.no:{{Ano}}</p>
- <button @click="test1">测试1</button>
- <button @click="test2">测试2</button>
- </div>
- <script>
- const store = new Vuex.Store({
- state:{no:100},
- mutations:{
- increment(state,no){state.no+=no;}
- },
- modules:{
- A:{
- namespaced:true,
- state:{no:50},
- mutations:{
- increment(state,no){state.no+=no;}
- }
- }
- }
- })
- var app = new Vue({
- el:'#app',
- store,
- computed:{
- ...Vuex.mapState({no:state=>state.no,Ano:state=>state.A.no})
- },
- methods:{
- ...Vuex.mapMutations(['increment']),
- ...Vuex.mapMutations('A',{incrementA:'increment'}),
- test1(){
- this.increment(10);
- },
- test2(){
-
- this.incrementA(100);
- }
- }
- })
- </script>
- </body>
- </html>
渲染如下:

这里虽然子仓库和根仓库都定义了increment,但是因为子仓库定义了namespaced,所以两个并不会起冲突,namespaced的作用就是将mutation和action和其它模块区分开来,引用时需要指定命名空间才可以
源码分析
module的收集是在Vuex.store()实例化时执行ModuleCollection.register()时完成的,如下:
- ModuleCollection.prototype.register = function register (path, rawModule, runtime) { //收集模块
- /*略*/
-
- // register nested modules
- if (rawModule.modules) { //如果rawModule.modules存在(含有子仓库)
- forEachValue(rawModule.modules, function (rawChildModule, key) {
- this$1.register(path.concat(key), rawChildModule, runtime); //递归调用register()注册子仓库
- });
- }
- };
这样就完成了模块的收集,安装模块时也会对子模块进行判断,如下:
- function installModule (store, rootState, path, module, hot) { //安装模块
- /*略*/
- module.forEachChild(function (child, key) { //如果有子模版
- installModule(store, rootState, path.concat(key), child, hot); //则递归调用自身
- });
- }
这样就完成模块的安装了。