vuex 源碼分析(七) module和namespaced 詳解


當項目非常大時,如果所有的狀態都集中放到一個對象中,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);        //則遞歸調用自身
    });
  }

這樣就完成模塊的安裝了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM