GitHub:
https://github.com/vuejs/vuex/blob/dev/src/module/module-collection.js
首先看构造函数:
constructor (rawRootModule) {
// register root module (Vuex.Store options)
this.register([], rawRootModule, false)
}
只是执行了一下register这个函数。借着看看这个函数做了什么
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 {
const parent = this.get(path.slice(0, -1))
parent.addChild(path[path.length - 1], newModule)
}
// register nested modules
if (rawModule.modules) {
forEachValue(rawModule.modules, (rawChildModule, key) => {
this.register(path.concat(key), rawChildModule, runtime)
})
}
}
get (path) {
return path.reduce((module, key) => {
return module.getChild(key)
}, this.root)
}
再去看看模块对应的getChild和addChild两个函数:
addChild (key, module) {
this._children[key] = module
}
getChild (key) {
return this._children[key]
}
两个函数都比较简单,重点在于register这个模块。
总体上来看register先通过get这个函数得到模块的父节点,将模块注册到父节点中。再通过深度优先遍历注册所有的模块。
但是怎么说呢,第一次看这个代码的时候我有点像是看语文的阅读理解,所有的字都认识,合起来就不知道什么意思。
通过一个例子来追踪一下register
假设我们有一个模块长这样:
a: {
modules: {
b: {
modules: {
c: {}, // c是一个模块
d: {} // d是一个模块
},
_children: []
}
},
_children: []
}
1 path的初始值是空。所以很明显,this.root就是a模块。
2 因为a模块的modules值不为空。所以会按循序注册modules里面的每一个模块。当注册b的时候path有[b].
2.1 b注册的时候,parent = this.get([])。也就是得到a模块
2.2 然后a模块添加b为子模块
2.3 这个时候b模块的modules不为空,所以按顺序注册b模块中modules里面的每一个模块。当注册到c的时候,path里面有[b,c]
2.3.1 c注册的时候,parent = this.get([b])。这个时候传入的数组不为空,我们按顺序看看返回的结果是什么。 首先,先明确 reduce函数的回调函数的第一个参数值是上一次的执行结果,第一个当前的数组元素。如果原来的get函数不好理解 的话,可以转换成for循环
var get = function (path) {
let module = this.root
for (key in path) {
if (module.getChild[key])
module = module.getChild[key]
}
return module
}
2.3.2 b模块添加c为子模块
2.3.3 假设c模块的modules为空,那么这个一个分支执行完毕
2.4.1 这时候栈回退到b模块下的d模块,按照上面的流程注册