如果你还不知道 Vuex 是怎么安装的,请移步 Vuex 源码学习(三)install 都做了哪些事情
整合模块
这一节该分析模块的是怎么被整合的,以及要整合成什么样子。
在 Vuex 的 constructor 中比较靠前的位置有这么两行代码,_modules 属性是 ModuleCollection 的实例对象,之后 _modules 属性被频繁使用,
这块就是对 Vuex 的模块进行了一次整合,整合出一个可以被使用的 _modules,而_moduleNamespaceMap 是一个空对象
该怎么整合模块?
先看一下我们我们项目中 Store 的结构 store/index.jsmoduleList:
moduleSet:
结构就是这样的在以上代码中的 modules 下的数据,我都称它是伪(未加工)模块,因为它还不具有模块的功能。当我们实例化 Vuex.Store 这个类的时候接收的参数 options 就会直接交给 moduleCollection 来处理。参数 options 是什么呢?就是上面图中这样结构的数据,想要处理成什么样子?下面看一下 ModuleCollection 是怎么处理的
export default class ModuleCollection {
constructor (rawRootModule) {
// register root module (Vuex.Store options)
// 注册模块并链接
this.register([], rawRootModule, false)
}
…
register (path, rawModule, runtime = true) {
if (process.env.NODE_ENV !== ‘production’) {
// 不符合规则的模块会报错。
assertRawModule(path, rawModule)
}
// 创建一个模块
const newModule = new Module(rawModule, runtime)
if (path.length === 0) {
this.root = newModule
} else {
// path.slice(0,-1) 就可以拿到父模块的 path。
// get 方法可以根据 path 来找到对应的模块。
const parent = this.get(path.slice(0, -1))
// 将子模块挂载到父模块上
parent.addChild(path[path.length – 1], newModule)
}
// register nested modules
if (rawModule.modules) {
// 遍历每个模块的 modules(目的是获取所有子模块)
forEachValue(rawModule.modules, (rawChildModule, key) => {
// 为什么要 path.concat(key)?
// 依次注册子模块。
this.register(path.concat(key), rawChildModule, runtime)
})
}
}
}
在 Vuex 与 vue-router 的源码中,命名变量是很有规律的,在开发人员使用这两个框架的时候,传递进去的参数,在使用时命名的变量名都是 raw 开头的,代表是未经过加工的。
将未经过加工的伪模块处理成真正可以使用的模块。
在初始化的时候直接开始注册模块,moduleCollection 的这个类的任务是把生成的模块合理的链接起来,而模块的生成交给了 Module 这个类。
所以 register 方法就是把根模块以及所有的子模块从一个伪(未加工)模块变成一个真正的模块并且链接起来。遍历树形结构用什么方法?递归!
register 都做了什么?
筛选出不符合规则的模块,报错提示。
将伪(未加工)模块加工成一个真正的模块。
将加工好的模块挂载在它的父模块上。
如果这个模块有 modules 属性(模块有自己的子模块)让每个子模块重复以上操作
递归的出口:rawModule.modules 为 false(模块没有子模块),也就是每个模块都没有子模块需要注册了,那就代表全部加工与链接完毕。
分析 register 的三个参数
register 接收三个参数,path、rawModule、hot。
hot 这个参数目前看来不关键。
rawModule 是伪(未加工)模块
那 path 的作用是什么呢?
path 的作用很大, 大家类比下前端页面的 dom 树的 Xpath,如果我想知道这个节点的位置,需要知道这个父节点的位置,然后一层一层的向上知道根结点,有了 Xpath 就可以直接找到这个节点,
Vuex 也是一样的想知道某个模块的位置,只需要提供根结点到他的一个 path,path 按顺序存储着根模块到它本身的所有祖先模块 (根模块没有名字,又不能把第一个放一个空,所以 path 里 面没有根模块),在每次注册的时候,这个模块有子模块,就把它的 path 加上(concat)子模块的名字,在子模块执行 register 方法时,path 就比它的父模块多一个父模块的名字,所以根模块注册的时候传入 path 就是 [](空数组)了。
ModuleCollection 的 get 方法可以根据 path 来获取指定的模块, 在挂载的时候十分有用,
,使用 reduce 的方法,按照数组的顺序,一层一层的找目标模块。
path 对以后要讲的设置命名空间也很有帮助。
总结
ModuleCollection 这个类,主要完成了模块的链接与整合,生成模块的任务交给了 Module 这个类。
模块的链接与整合通过递归完成。
path 可以让 moduleCollection 快速找到对应模块。
下一章讲述生成的 module 具体可以做什么
我是一个应届生,最近和朋友们维护了一个公众号,内容是我们在从应届生过渡到开发这一路所踩过的坑,已经我们一步步学习的记录,如果感兴趣的朋友可以关注一下,一同加油~