通俗易懂Vuex源码导读2.1-installModule模块安装及内容建立

installModule(模块安装函数)

  • 参数介绍html

    • store:store 对象实例,new Vuex.Store({...})生成的对象
    • rootState:根模块的state对象
    • path:上一章节介绍过的,当前模块所处层级数组
    • module:模块对象
    • hot:暂时用不上,先不介绍
  • 判断当前模块是否根模块

    图片

  • 获取模块的命名空间vue

    • 调用状态模块树的 getNamespace 方法,传入当前模块的模块层级数组 path
    • getNamespace 和以前提到的 get 方法相似,一样是从根模块出发,根据给出的路径数组,递归每个层级的模块

      图片

    • 在每个模块中,判断是否设置命名空间变量namespaced
    • 有命名空间,则以模块名结合"/",造成命名空间路径,即本函数是获取模块的命名空间前缀,如对应下图,moduleC的命名空间前缀为'moduleA/moduleC/',其中moduleB并无设置命名空间vuex

      {
        moduleA:{
          namespaced:true,
          modules:{
            moduleB:{
                namespaced:false,
                modules:{
                  moduleC:{
                    namespaced:true,
                  }
                }
              }
          }
        }
      }
    • 有些同窗可能会疑惑,module对象里面,没有定义namespaced变量啊?其实namespaced是经过 get拦截器定义的,获取namespaced时,真正获取的是this._rawModule.namespaced,即module对象保存的配置信息中namespaced的值

      图片

  • 若是当前模块设置了命名空间,则将本模块存入当模块命名容器对象_modulesNamespaceMap中,并以刚刚生成的命名空间前缀名为key。注意,若是当前模块没有设置命名空间,即便父模块设置了,也不须要加入到_modulesNamespaceMap中,例如moduleB不会被加入到_modulesNamespaceMap中

    图片

  • 对于非根模块segmentfault

    • 经过getNestedState,寻找父模块的 state对象。api

      • getNestedState参数介绍,rootState是根容器的state,path.slice(0, -1)是除去本模块后的模块层级数组
      • 与get函数、getNamespace函数相似,从state中根据path层级数组,每层递归寻找,找到对应的state,在这里找的是父模块的state对象
      • 从这个操做,咱们能够猜想,state也将和module模块树同样,经过state造成一个state树,其树结构和module结构相对应
    • 获取本模块的模块名
    • 调用store的_withCommit函数数组

      • 该函数的操做很简单,只是将设置_committing标识符为ture,而后执行某函数,函数执行完在将_committing设置为原来的值
      • 即容许在函数函数执行期间,修改state的值
      • _committing标识符在介绍全局变量时介绍过,用于在严格模式时,防止非commit方式修改state

        图片

    • 在_withCommit函数中数据结构

      • 借用Vue的set方法,Vue 在install中被传入并保存
      • 往父State对象中,添加以模块名为key,为模块内state对象为value的变量,借用Vue.set方法,实现数据响应
      • 注意,因为是直接往父模块的State中添加变量,当父模块的state中有与插入的模块同名的变量时,state中变量将被覆盖。

        图片

    • 结合 Vue.set(parentState, moduleName, module.state) 及 getNestedState函数,咱们能够肯定state也和module模块树同样,经过state造成一个state树,其树结构和module结构相同。
    • 这就是为何获取子模块的state变量时,是经过this.$store.state.moduleName.xxx来获取,对应官网
  • 建立模块内容,makeLocalContext(store, namespace, path)。并将生成的content内容放在module.content中。咱们跳转这里,看看makeLocalContext具体操做app

    • 模块内容,返回命名空间前缀处理后的getters,state,commit,dispatch方法
  • 调用module.forEachMutation方法,对模块的mutation方法进行注册ide

    图片

    • registerMutation注册函数函数

      • 参数介绍,store是store实例, type是mutation函数名, handler是mutation的函数体,即实际执行的函数, local是刚刚生成的模块的local对象
      • store._mutations[type],以mutation函数名为key,在store根实例的_mutations容器对象中,添数组容器。存放全部同名的mutation函数
      • 因此mutation是不怕重名的,重名将逐个执行调用。这个在官网中没有作介绍
      • 往mutation函数数组中加入一个函数,函数的参数就是咱们在使用commit时的第二个参数,载荷参数
      • 在函数内部,修改this,并传入local对象做为参数,这也是为何咱们在commit的时候只传递了负载参数,但在实际执行mutation的时候还能够接收到本模块的state参数
      • 总的来讲,收集了配置函数中的mutation函数,统一经过store实例的_mutations变量保存
        图片
  • 调用module.forEachAction方法,对模块的action进行注册

    • 注册action的操做和注册Mutation的操做大致相同,一样是经过一个变量收集全部模块的action函数
    • 只是注册action时,多传递些参数

      图片

  • 调用registerGetter,注册每个getter方法,经过_wrappedGetters收集

    图片

  • 最后是循环注册全部模块
  • 小结,总的来讲,是收集模块配置文件中的每个mutation项,每个action,每个getter。往回调函数中添加参数

    图片

makeLocalContext,建立模块内容

  • 参数介绍

    • store, store 对象实例
    • namespace, 模块的命名空间前缀,无论本模块是否设置命名空间,父模块设置了,也会该值
    • path,模块层级关系数组,注意,namespace与path并不必定相同,由于命名层级,只有当模块设置有命名空间,才存在对应层级命名
  • 定义local对象

    • 定义local对象的dispatch函数

      • 根据namespace(本模块及祖先容器是不是设置过命名空间)进行判断
      • 没有设置,则直接使用 Store实例的 dispatch函数
      • 有命名空间,则在使用 Store 实例的dispatch函数前,对参数进行一些处理

        • 统一参数形式 unifyObjectStyle,type是调用dispatch方法的一个参数,即action函数名,payload是负载变量,options是配置项。unifyObjectStyle函数的原理就是,判断参数是否为对象,是对象则进行解析,并调整参数位置
        • 根据options.root判断是否往全局发送的action函数,对应官网
      • 若是不是发送全局的action函数,即只发送本模块内的action函数,这是往调用的的 action函数名中,添加命名空间路径前缀。例如对应下图,actionC函数在_actions中,则被保存为'/moduleA/moduleC/actionC'(具体保存过程将在后续介绍到)。在调用时,使用方法为this.$store.dispatch('/moduleA/moduleC/actionC')。对应官网介绍
      {
        moduleA:{
          namespaced:true,
          modules:{
            moduleB:{
                namespaced:false,
                modules:{
                  moduleC:{
                    namespaced:true,
                     actions: {
                        actionC (context) {...}
                      }
                  }
                }
              }
          }
        }
      }
      • 最后,调用store实例的dispatch函数

        图片

    • 定义commit函数

      • 与定义dispatch函数相似,有命名空间,则往mutation函数前添加命名空间前缀
      • 最后也而是调用store实例的commit函数

        图片

  • 调用Object.defineProperties函数,往local对象中添加两个变量getters和state,设置local对象的getter和state

    图片

    • Object.defineProperties函数用于往某对象中添加属性,并设置该数据的访问拦截器,即访问该数据时,将先调用拦截器函数,defineProperties函数详细介绍
    • 定义getters变量,并设置其拦截器。一样的,判断是否有命名空间前缀

      • 没有,则直接访问store 对象实例的getters变量
      • 不然调用makeLocalGetters函数,生成本身的getters

        • 参数介绍,store是 store 对象实例,namespace是命名空间前缀
        • 先定义一个对象 gettersProxy,译为 getters代理
        • 遍历store.getters中的全部函数,(store.getters变量暂时还未定义到,其指向 store的_wrappedGetters,getter函数容器)

          • 检测各个函数名,判断该函数名的前缀是否与模块的命名空间前缀相同。找到即符合命名前缀的函数。
          • 去除命名空间前缀,能获取纯粹的getter函数名
          • 一样,经过Object.defineProperty,往刚刚定义的gettersProxy对象中,添加变量,并设置其get拦截器。enumerable是其中一个配置项目,设置当使用for等枚举循环时,是否显示该变量
          • 以带命名空间前缀的getter名为key,添加到gettersProxy对象中,当访问它时,返回是,从Store.getters(即store._wrappedGetters,getter容器)中提取的带上命名空间前缀的getter函数
          • 因此说,其实全部的getter都存放在了Store实例的_wrappedGetters变量中,如模块没有命名空间前缀,则直接存入。不然将getter函数名带上命名空间前缀后,再加入进去
        • 小结,

          • makeLocalGetters的做用,是将经过模块拿getter时,如何经过store.getters中取。是否应该添加前缀、
    • 定义state,及其拦截器

      • state的获取,则不须要命名空间前缀的识别,并且直接经过getNestedState,从state树结构中取
      • 这是由于state和getter的存储方式不同,state是独立的状态树,有明显的层级关系
      • getters则所有都放在store实例的getters变量中保存,属于同层级,只是经过函数前缀进行了区分(有设命名空间的话)
      • state是根据层级关系设置,getters则更具命名空间区分,与层级关系不大
        图片
  • 小结

    • 定义可local变量,有设置命名空间,则往各个函数名中添加命名空间前缀
    • 对应数据结构中,全局容器的介绍就更清晰了,实际操做的函数仍是那个函数,这里只是在调用函数前,对参数进行一些调整
    • 设置了getters,state,commit,dispatch方法的中间处理
  • 啊,一口气看无缺累,歇歇
    图片

总目录

相关文章
相关标签/搜索