• 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);        //则递归调用自身
        });
      }

    这样就完成模块的安装了。

  • 相关阅读:
    Smarty中的请求变量和保留变量的使用范例
    mysql通过sql语句判断某个字段在一张表中是否存在
    dede用户登录时,跳转到提示页时报404错误
    eclipse自动补全的设置
    Eclipse使用技巧
    JS中比较的数值如何比较大小
    在文本框中提示用户输入内容格式的方法
    使用命令行创建Android工程报错:"Target id is not valid. Use 'android.bat list targets' to get the target ids"
    eclipse下 Failed to find an AVD compatible with target 的解决方法
    如何更改Android的默认虚拟机地址(Android virtual driver路径设置)
  • 原文地址:https://www.cnblogs.com/greatdesert/p/11424930.html
Copyright © 2020-2023  润新知