• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

seajs模块之间依赖的加载以及模块的执行

php 搞代码 4年前 (2022-01-04) 31次浏览 已收录 0个评论

本文介绍的是seajs模块之间依赖的加载以及模块的执行,下面话不多说直接来看详细的介绍。

入口方法

每个程序都有个入口方法,类似于c的main函数,seajs也不例外。系列一的demo在首页使用了seajs.use() ,这便是入口方法。入口方法可以接受2个参数,第一个参数为模块名称,第二个为回调函数。入口方法定义了一个新的模块,这个新定义的模块依赖入参提供的模块。然后设置新模块的回调函数,用以在loaded状态之后调用。该回调函数主要是执行所有依赖模块的工厂函数,最后在执行入口方法提供的回调。

// Public API// 入口地址seajs.use = function(ids, callback) { Module.preload(function() { Module.use(ids, callback, <p style="color:transparent">来源gao!daima.com搞$代!码网</p>data.cwd + "_use_" + cid()) }) return seajs} // Load preload modules before all other modulesModule.preload = function(callback) { var preloadMods = data.preload var len = preloadMods.length  if (len) { Module.use(preloadMods, function() {  // Remove the loaded preload modules  preloadMods.splice(0, len)   // Allow preload modules to add new preload modules  Module.preload(callback) }, data.cwd + "_preload_" + cid()) } else { callback() }} // Use function is equal to load a anonymous moduleModule.use = function (ids, callback, uri) { var mod = Module.get(uri, isArray(ids) ? ids : [ids])  mod.callback = function() { var exports = [] var uris = mod.resolve()  for (var i = 0, len = uris.length; i < len; i++) {  exports[i] = cachedMods[uris[i]].exec() } // 回调函数的入参对应依赖模块的返回值 if (callback) {  callback.apply(global, exports) }  delete mod.callback }  mod.load()}

Module.preload用于预加载seajs提供的插件plugins,非主要功能,可以忽略。而Module.use则是核心方法,该方法正如之前所说,创建新的module并设置回调函数,最后加载新模块的所有依赖模块。

加载依赖之load方法

load方法可谓是seajs的精华所在。该方法主要加载依赖模块并依次执行依赖模块的回调函数,最终执行的回调函数则是通过seajs.use(“./name”)创建的新模块的回调,也就是mod.callback 。

load方法递归加载依赖模块,如果依赖模块还依赖其他模块,则再加载这个模块。这是通过Module类中的_waitings和_remain来实现的。

Module.prototype.load = function() { var mod = this  // If the module is being loaded, just wait it onl oad call if (mod.status >= STATUS.LOADING) { return }  mod.status = STATUS.LOADING  // Emit `load` event for plugins such as combo plugin var uris = mod.resolve() emit("load", uris, mod)  var len = mod._remain = uris.length var m  // Initialize modules and register waitings for (var i = 0; i < len; i++) { m = Module.get(uris[i])  // 修改 依赖文件 的 _waiting属性 if (m.status < STATUS.LOADED) {  // Maybe duplicate: When module has dupliate dependency, it should be it's count, not 1  m._waitings[mod.uri] = (m._waitings[mod.uri] || 0) + 1 } else {  mod._remain-- } }  // 加载完依赖,执行模块 if (mod._remain === 0) { mod.onload() return }  // Begin parallel loading var requestCache = {}  for (i = 0; i < len; i++) { m = cachedMods[uris[i]]  // 该依赖并未加载,则先fetch,将seajs.request函数绑定在对应的requestCache上,此时并未加载模块 if (m.status < STATUS.FETCHING) {  m.fetch(requestCache) } else if (m.status === STATUS.SAVED) {  m.load() } }  // Send all requests at last to avoid cache bug in IE6-9. Issues#808 // 加载所有模块 for (var requestUri in requestCache) { if (requestCache.hasOwnProperty(requestUri)) {  // 此时加载模块  requestCache[requestUri]() } }} // 依赖模块加载完毕执行回调函数// 并检查依赖该模块的其他模块是否可以执行Module.prototype.onload = function() { var mod = this mod.status = STATUS.LOADED  if (mod.callback) { mod.callback() } console.log(mod) // Notify waiting modules to fire onl oad var waitings = mod._waitings var uri, m  for (uri in waitings) { if (waitings.hasOwnProperty(uri)) {  m = cachedMods[uri]  m._remain -= waitings[uri]  if (m._remain === 0) {  m.onload()  } } }  // Reduce memory taken delete mod._waitings delete mod._remain}

首先初始化模块的_waitings和_remain属性,如果_remain为0,则意味着没有依赖或者依赖已加载,可以执行onload函数;如果不为0,则fetch未加载的模块。在这里有个实现的小技巧,就是同时加载所有依赖:requestCache对象保存加载函数:(在fetch函数中定义)

if (!emitData.requested) { requestCache ?  requestCache[emitData.requestUri] = sendRequest :  sendRequest() }

其中,sendRequest函数定义如下:

function sendRequest() { seajs.request(emitData.requestUri, emitData.onRequest, emitData.charset) }

并行加载所有依赖,当依赖加载完毕,执行onRequest回调,向上冒泡,加载依赖的依赖,直至没有依赖模块。

当最上层的依赖已没有依赖模块时,执行onload函数,在函数体内设置状态为loaded,执行mod.callback,并检查并设置该模块的_waitings属性,判断下层模块是否还有依赖,若没有则执行下层模块的mod.callback,这一依次回溯,最终将会执行通过seajs.use创建的匿名模块的mod.callback。

例证

通过一个简单的例子,论证上述过程:

tst.html <script>  seajs.use('./b');</script>-------------------------------------a.js define(function(require,exports,module){ exports.add = function(a,b){  return a+b; }})------------------------------------b.js define(function(require,exports,module){ var a = require("./a"); console.log(a.add(3,5));})

通过调试工具,可以看出执行onload的次序:

最后可看出,匿名模块的状态码为4,也就是该模块并未执行.确实,也没有给匿名模块定义工厂函数,无法执行.

模块执行之exec

模块执行是在seajs.use中定义的mod.callback中调用的,依次调用所有依赖的exec方法,执行程序逻辑。exec方法中有commonJS的一些重要关键字或者函数,如require,exports等,让我们一看究竟:

Module.prototype.exec = function () { var mod = this  // When module is executed, DO NOT execute it again. When module // is being executed, just return `module.exports` too, for avoiding // circularly calling if (mod.status >= STATUS.EXECUTING) { return mod.exports }  mod.status = STATUS.EXECUTING  // Create require var uri = mod.uri  function require(id) { return Module.get(require.resolve(id)).exec() }  require.resolve = function(id) { return Module.resolve(id, uri) }  require.async = function(ids, callback) { Module.use(ids, callback, uri + "_async_" + cid()) return require }  // Exec factory var factory = mod.factory  // 工厂函数有返回值,则返回; // 无返回值,则返回mod.exports var exports = isFunction(factory) ?  factory(require, mod.exports = {}, mod) :  factory  if (exports === undefined) { exports = mod.exports }  // Reduce memory leak delete mod.factory  mod.exports = exports mod.status = STATUS.EXECUTED  // Emit `exec` event emit("exec", mod)  return exports}

require函数获取模块并执行模块的工厂函数,获取返回值。require函数的resolve方法则是获取对应模块名的绝对url,require函数的async方法异步加载依赖并执行回调。对于工厂方法的返回值,如果工厂方法为对象,则这就是exports的值;or工厂方法有返回值,则为exports的值;or module.exports的值为exports的值。当可以获取到exports值时,设置状态为executed。

值得注意的一点:当想要通过给exports赋值来导出一个对象时

define(function(require,exports,module){ exports ={  add: function(a,b){    return a+b;  } }})

是不成功的.我们通过执行上述方法来判断最终导出exports的值.首先,函数没有返回值,其次,mod.exports为undefined,最终导出的exports为undefined。为什么会出现这种情况呢?是因为js中引用赋值所造成的。js的赋值策略是“按共享传递”,虽说初始时exports === module.exports,但是当给exports赋一个对象时,此时exports指向该对象,module.exports却仍未初始化,为undefined,因此会出错。

正确的写法为

define(function(require,exports,module){ module.exports ={  add: function(a,b){    return a+b;  } }})

总结

可以说,seajs的核心模块的实现已讲解完毕,见识了不少编码技巧,领略了回调模式的巧妙,以及于细微处的考量。代码的每一处都考虑到了内存泄露和this指针引用偏移的危险,做了积极的预防,这种精神值得学习。


搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:seajs模块之间依赖的加载以及模块的执行

喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址