加载seajs模块之间的依赖关系和模块的执行
本文介绍了seajs模块间依赖关系的加载和模块的执行。详细介绍我就不多说了。
进入方法
每个程序都有一个入口方法,类似于c的主函数,seajs也不例外。系列一演示在首页使用seajs.use()作为入口方法。entry方法可以接受两个参数,第一个参数是模块名,第二个参数是回调函数。entry方法定义了一个新的模块,它依赖于输入参数提供的模块。然后设置加载状态后要调用的新模块的回调函数。这个回调函数主要执行所有依赖模块的工厂函数,最后执行entry方法提供的回调。
//Public API//入口地址seajs.use=function (ids,回调){ module . preload(function(){ module . Use(ids,回调,data . CWD ' _ Use ' _ cid())})返回seajs}//在所有其他模块之前加载预加载模块preload module . preload=function(回调){ var preload MODS=data . preload var len=preload MODS . len if(len){ module . Use(preload MODS,function() { //移除加载的预加载模块preloadMods.splice(0,len) //Allowids :[ids])mod . callback=function(){ var exports=[]var uri=mod . resolve()for(var I=0,len=uris.length我透镜;I){ exports[I]=cached ODS[URIs[I]。exec()}//回调函数的输入对应于if(callback){ callback . apply(global,Exports)} delete mod的返回值。回调} mod。load ()}模块。preload用于预加载seajs提供的插件,这不是主要功能,可以忽略。而Module.use是核心方法,如前所述,它创建一个新模块并设置回调函数,最后加载新模块的所有依赖模块。
负载相关负载方法
加载方法是seajs的精髓。该方法主要加载依赖模块并顺序执行依赖模块的回调函数,最终执行的回调函数是seajs.use()创建的新模块的回调。/name”),即mod.callback
load方法递归加载依赖模块,如果依赖模块也依赖于其他模块,则再次加载该模块。这是通过等待和保持在模块类中来实现的。
模块。原型。load=function(){ var mod=this//如果正在加载模块,只需等待它加载调用if (mod.status=STATUS .加载){返回}模式状态=状态加载//为组合插件var uri=mod。resolve()Emit(' load ',uri,mod) var len=mod等插件发出负荷事件_ retain=uri。长度变化m/初始化模块并注册等待(var I=0;我透镜;I){ m=模块。get(uri[I])//修改依赖文件的等待(_ w)属性if (m.status STATUS .LOADED) { //可能是复印机:当模块有双重依赖时,应该是它的计数,而不是1m。_ waitings[mod。uri]=(m . _ waitings[mod。uri]| | 0)1 } else { mod ._保持- } } //加载完依赖,执行模块if (mod ._ rest===0){ mod。onload()} return }//开始并行加载var请求缓存={ }为(I=0;我透镜;I){ m=cachedMods[uri[I]]//该依赖并未加载,则先获取,将seajs.request函数绑定在对应的requestCache上,此时并未加载模块if (m.status STATUS .取数){ m . fetch(Request cache)} else if(m . STatus===STatus .SAVED) { m.load() } } //最后发送所有请求,避免IE6-9中的缓存bug .问题#808 //加载所有模块for(请求缓存中的var请求uri){ if(请求缓存。hasown属性(请求uri)){//此时加载模块请求缓存[请求uri]()} } }//依赖模块加载完毕执行回调函数//并检查依赖该模块的其他模块是否可以执行模块。原型。onload=function(){ var mod=this mod。状态=状态.已加载if(mod。回调){ mod。回调()}控制台。日志(mod)//通知等待模块在加载变量等待=mod时激发waitings var uri,m代表(waitings中的uri){ if(waitings。hasown属性(uri)){ m=cachedMods[uri]m . _ remain-=waitings[uri]if(m . _ remain===0){ m . onload()} }//减少内存占用删除mod ._等待删除国防部._保持}首先初始化模块的等待时间(_ w)和_保持属性,如果_保持为0,则意味着没有依赖或者依赖已加载,可以执行装载函数;如果不为0,则取得未加载的模块。在这里有个实现的小技巧,就是同时加载所有依赖:requestCache对象保存加载函数:(在取得函数中定义)
if(!emitdata。请求){请求缓存?请求缓存[emitdata。请求uri]=发送请求:发送请求()}其中,发送请求函数定义如下:
函数send request(){ seajs。请求(emitdata。请求uri,emitData.onRequest,emitData.charset) }并行加载所有依赖,当依赖加载完毕,执行请求回调,向上冒泡,加载依赖的依赖,直至没有依赖模块。
当最上层的依赖已没有依赖模块时,执行装载函数,在函数体内设置状态为已加载,执行适度的回调,并检查并设置该模块的等待时间(_ w)属性,判断下层模块是否还有依赖,若没有则执行下层模块的适度的回调,这一依次回溯,最终将会执行通过seajs.use创建的匿名模块的适度的回调。
例证
通过一个简单的例子,论证上述过程:
tst.htmlscript seajs.use(' ./b ';/script-a . jsdefine(function(require,exports,module){ exports.add=function(a,b){ return a b;}}) - b.jsdefine(函数(要求,导出,模块){ var a=要求' ./a ');console.log(a.add(3,5));})通过调试工具,可以看出执行装载的次序:
最后可看出,匿名模块的状态码为4,也就是该模块并未执行。确实,也没有给匿名模块定义工厂函数,无法执行。
模块执行之高级管理人员
模块执行是在seajs.use中定义的适度的回调中调用的,依次调用所有依赖的高级管理人员方法,执行程序逻辑高管。方法中有commonJS的一些重要关键字或者函数,如要求,出口等,让我们一看究竟:
module . prototype . exec=function(){ var mod=this//执行模块时,不要再次执行。当正在执行模块//时,也只需返回“module.exports”,以避免循环调用if (mod.status=STATUS)。正在执行){ return mod . exports } mod . STATUS=STatus。正在执行//创建require var uri=mod.uri函数require(id) {返回Module.get(require.resolve(id))。exec()} require . resolve=function(id){ return module . resolve(id,Uri)} require . async=function(ids,回调){ Module.use(ids,回调,Uri ' _ async _ ' cid())return require }//exec factory varfactory=mod.factory//if工厂函数有返回值,它将返回;//如果没有返回值,那么mod。exports var exports=isfunction(工厂)?factory(require,mod.exports={},mod): factory if(exports===undefined){ exports=mod . exports }//减少内存泄漏delete mod . factory mod . exports=exports mod . status=status . executed//emit ` exec ` event emit(' exec ',mod) return exports} require函数获取模块并执行模块的factory函数以获取返回值。require函数的resolve方法获取相应模块名称的绝对url,require函数的async方法异步加载依赖项并执行回调。对于工厂方法的返回值,如果工厂方法是一个对象,那么这就是导出的值;如果或工厂方法有返回值,则为导出的值;或模块的值。exports是导出的值。当可以获得导出值时,将状态设置为已执行。
值得注意的是,当您想要通过为导出赋值来导出对象时,
define(function(require,exports,module){ exports={ add : function(a,b){ return a b;}}})不成功。我们通过执行上述方法来判断最终出口产品的价值。第一,函数没有返回值;其次,mod.exports是未定义的,最终导出的导出是未定义的。为什么会这样?这是由js中的引用赋值引起的。js的分配策略是“共享通过”。虽然exports===module.exports最初,当exports被分配一个对象时,exports指向该对象,但是module.exports仍然是未初始化和未定义的,因此会出现错误。
正确的写法是
define(function(require,exports,module){ module . exports={ add : function(a,b){ return a b;}}})摘要
可以说seajs核心模块的实现已经讲解过了,看到了很多编码技巧,也欣赏到了回调模式的精妙之处。代码的每一部分都考虑了内存泄漏的危险和这个指针引用的偏差,并采取了积极的预防措施。这种精神值得学习。
对于seajs来说,我花了不下一周的时间来阅读源代码。从最初的知识到现在的崇拜,我才真正意识到设计理念的重要性。之前,我没有太关注实现的技术性。我以为它可以实现没有bug,并且有很好的健壮性,但是现在我意识到我错了,尤其是在负载依赖模块的实现上。以上就是本文的全部内容。希望这篇文章的内容对你的学习或工作有所帮助。有问题可以留言交流。
版权声明:加载seajs模块之间的依赖关系和模块的执行是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。