當項目非常大時,如果所有的狀態都集中放到一個對象中,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); //則遞歸調用自身 }); }
這樣就完成模塊的安裝了。
