在Store的构造函数里面,有这么一个实例构建的过程
经过这个实例的构建,上图中的options会以下图的方式存储在store._mudules中
看出来区别了吗? 层级结构没变,也就是说child还是子集,但是options和child对象的结构有了变化
为了容易说明,下文中所说的普通module对象就是指上文中的options/child,所说实例module就是下图中的包含rawModule、state、child等属性的实例module,实例module是通过new Module(参数为普通module)得到的。
既然提到了,可以先说说Module构造函数了
var Module = function Module (rawModule, runtime) {
this._children = Object.create(null);
this._rawModule = rawModule;
var rawState = rawModule.state;
this.state = (typeof rawState === 'function' ? rawState() : rawState) || {};
};
先一张图来看看普通module和实例module之间的关系
可以看到,其实构造module和module的关系就是
- 前者的属性rawModule的值就是其对应的整个完整的普通module,
- state的值是其对应的普通module的state属性,
- 在普通module中,module的子module以键值对的形式存在父级的modules属性中,而在实例module中,每一个子module被构建成实例module,存放在其父级的child属性里
- 同时,Module还有两个原型方法与这次要说的知识相关的函数: addChild()和通过key来getChild()来设置和获取子级
Module.prototype.addChild = function addChild (key, module) {
this._children[key] = module;
};
ModuleCollection.prototype.get = function get (path) {
return path.reduce(function (module, key) {
return module.getChild(key)
}, this.root)
};
上面的一系列转换,方便了ModuleCollection的get方法和间接地使接下来installModule方法可以来递归获取深层级的子级(这个方法下一次再说)
了解完Module构造函数之后,就可以回到ModuleCollection了
var ModuleCollection = function ModuleCollection (rawRootModule) {
this.register([], rawRootModule, false);
};
ModuleCollection.prototype.register = function register (path, rawModule, runtime) {
var this$1 = this;
//无关代码省略
var newModule = new Module(rawModule, runtime); // 先把普通module变成对应的实例module
//假如没有path长度,说明是第一次调用,因为第一次在ModuleCollection构造函数中是这样调用的
//this.register([], rawRootModule, false);
//而从第二层级开始,path参数就会插入子级的key
//this$1.register(path.concat(key), rawChildModule, runtime);
if (path.length === 0) {
this.root = newModule;
} else {
var parent = this.get(path.slice(0, -1));
parent.addChild(path[path.length - 1], newModule);
}
// register nested modules
if (rawModule.modules) {
forEachValue(rawModule.modules, function (rawChildModule, key) {
this$1.register(path.concat(key), rawChildModule, runtime);
});
}
};
在第一次调用register函数的的时候
this.register([], options, false);
options作为参数传入,options会作为参数创造出一个实例module(假设命名A),在里面因为path的长度为0,所以this.root就是A. options的module属性有子module child,所以要第二次register
this$1.register(path.concat(key), rawChildModule, runtime);
实参是这样的
this$1.register(['child'], child, false);
因为path长度不为0,所以需要执行以下代码
var newModule = new Module(rawModule, runtime);
var parent = this.get(path.slice(0, -1));
parent.addChild(path[path.length - 1], newModule);
实参是这样的
var newModule = new Module(child, false);
var parent = this.get([]);
parent.addChild('child', newModule);
假如说普通module child还有modules属性,里面有一个 key为grandChild的普通module(B),就会第三次执行register函数
this$1.register(['child','grandChild'], B, false);
path不为空,所以也是会执行以下函数
var newModule = new Module(B, false);
var parent = this.get(['child']);
parent.addChild('grandChild', newModule);
一直到这一层级的普通module没有modules属性或者该属性里面没有内容,递归便结束了,这个new ModuleCollection的构造过程也结束了
上面有一个get函数,其实就是运用了Module构造函数的getChild方法
ModuleCollection.prototype.get = function get (path) {
return path.reduce(function (module, key) {
return module.getChild(key)
}, this.root)
};
上面就是this.get函数,入参是path,举个例子
- 假如path为[],长度为0,reduce会直接返回this.root
- 假如path为['child'],长度为0,reduce会直接返回this.root,然后通过this.root.getChild('child'),得到this.root['child']
- 假如path为['child','grandChild'],reduce会在上面返回的this.root['child'],通过this.root['child'].getChild('grandChild')获得this.root['child']['grendChild']
- 假如path为['child','grandChild','xxx'],同理会返回this.root['child']['grandChild']['xxx']
- 依次类推..
和上面的get方法类似实现原理的还有getNameSpace这个方法
ModuleCollection.prototype.getNamespace = function getNamespace (path) {
var module = this.root;
return path.reduce(function (namespace, key) {
module = module.getChild(key);
return namespace + (module.namespaced ? key + '/' : '')
}, '')
};
先看看方法执行的效果
- 可以看到 这个普通module的namespaced为falsy的话,那getNamespace返回的值讲和它的父级是一样的,但是这个返回的值在module.namespaced为falsy的时候根本不会被使用,所以有相同的也无所谓了
// module.namespaced为falsy的时候,
// namespace只会拼接上一次reduce返回的namespace和'',
// namespace+''===namespce
return namespace + (module.namespaced ? key + '/' : '')
举个例子上图中的child/grand2xChild/是怎么得到的
this.getNamespace(['child','grandChild','grandChildChild']) 入参path是['child','grandChild','grandChildChild'] path.reduce第一次
module = module.getChild(key); // module为this.root['child'],
return namespace + (module.namespaced ? key + '/' : '') /这个module的namespaced为true,key是'child',所以namespace = ''+'child'+'/'('child/'),这个namesapce将在第二次reduce中使用
path.reduce第二次
module = module.getChild(key);//module为this.root['child']['grandChild'],
return namespace + (module.namespaced ? key + '/' : '')//,这个module的namespaced为undefined,所以namespace = 'child/' + '' ,还是'child/',这个namesapce将在第三次reduce中使用
path.reduce第三次
module = module.getChild(key);//module为this.root['child']['grandChild']['grandx2child'],
return namespace + (module.namespaced ? key + '/' : '')//,这个module的namespaced为true,key是grandx2child,所以namespace = 'child/'+'grandx2child'+'/' ,('child/grandx2child/')
好了,最后看不明白的可以去瞅瞅reduce这个函数了解一下,再看不懂那就是我的问题了。。
总结来说ModuleCollection做了以下几件事情
- 通过register方法,转换普通module为Module的实例,并按照普通module的父子层级关系建立this.root对象
- 注册了get和getNameSpace方法。(当然还有其他的一些方法,但和本文关系不大)