seajs源码理解(二)
module.js是整个seajs最核心的部分,包含主要api的实现。我们先简化下这段的结构如下:
//模块构建类 function Module(uri, deps) { } //模块对象的实例函数,用于解析依赖 Module.prototype.resolve = function(){ //内部调用工具方法获取依赖 var uris = Module.resolve() return uris; } //加载依赖 Module.prototype.load = function(){ //获取所有依赖 mod = this; var uris = mod.reslove();//返回依赖数组 //循环加载依赖 for(){ Module.get //获取这个依赖 m.fetch()//加载这个依赖 m.load()//加载依赖的依赖 } //最后触发模块的onload mod.onload() } //加载完毕后回调 Module.prototype.onload = function(){ //触发callback mod.callback() } //真正获取模块 Module.prototype.fetch = function(){} //执行模块 Module.prototype.exec = function(){ //获取factory var factory = mod.factory; //执行时传入三个参数require、exports、module factory(require, mod.exports = {}, mod) //返回值 return exports } //工具方法 //id转为绝对url函数 Module.resolve = function(){ //实际上调用的是seajs.resolve seajs.resolve() } //申明模块 Module.define = function(){ //获取代码内的require依赖 if (!isArray(deps) && isFunction(factory)) { deps = parseDependencies(factory.toString()) } //内部调用Module.save保存模块 Module.save() } //保存模块 Module.save = function(){ //获取模块,保存初始化信息 var mod = Module.get(uri) mod.id = ... mod.dependencies = ... mod.factory = ... } //获取模块 Module.get = function(){ //从缓存获取,如果没有就创建 } //use一个模块 Module.use = function(){ //获取该模块 var mod = Module.get(); //绑定回调 mod.callback = function(){} //加载依赖 mod.load() } //预加载模块 Module.preload = function(){ //调用use加载预加载模块 Module.use(); } //暴露api global.define = Module.define seajs.use = function(){} seajs.require = function(){}//没有作为对外api使用,但是可以这么用
接下来通过看两个api的执行过程,了解上述所有函数,一个是seajs.use(),一个是define()
seajs.use(url,function(){});调用过程:
1.先调用Module.preload加载最先应当加载的依赖,就是前文所说的带在url上的那种。preload的内部还是会调用第二步中的Module.use
2.调用Moduel.use;这里做了三件事
1).调用Module.get生成模块对象mod,并附上唯一id
2).为模块绑定好callback,为加载完之后调用(这里的callback不仅把seajs.use传入的callback放进去执行,还把所有的依赖模块执行后的结果传入到callback参数中)
3).调用mod.load()实例方法开始加载依赖。
3.进入mod.load()加载依赖,具体如下:
1).调用实例方法mod.resolve获取所有依赖数组。
mod.resolve的过程又如下:
a.调用工具函数Module.resolve
b.工具函数Module.resolve内部调用seajs.resolve
c.seajs.resolve=id2url
d.上文util-path.js中id2url内部调用了parseAlias各种转换函数得到依赖的绝对路径。
2).为每个依赖生成模块对象
3).所有依赖都加载完毕时,执行mod.onload()
onload内部执行2的2)中已经为该模块绑定好的callback
4).依赖未全部加载完,执行mod.fetch()
fetch()的内部实际使用的是seajs.request(),但是这里只是进行了循环绑定,还未执行,就是上文util-request.js中动态插入js标签的方法。
5).全部循环执行过fetch后,最后统一再次循环执行插入js的操作。
define('路径',function(){}),define挂载在global(也就是window)下,所以我们引入seajs后可以直接使用define(),他的执行过程如下:
1.global.define = Module.define
2.Module.define内部只做了一件事,调用Module.save存储该模块相关信息。
3.Module.save从简化结构可以看出他的作用是给生成的模块对象实例写入一些有用的信息,供之后使用。
以上基本是use和define两个api的执行过程和调用函数链,基本覆盖了代码结构中的所有函数调用,需要特殊指出的是Module.prototype.exec模块对象执行函数。
Module.prototype.exec函数只是在模块绑定callback回调时用到,为模块加载依赖时,首先每个依赖都会生成Module实例,需要让每个依赖实例调用执行方法,我们才能拿到这些依赖的返回值。