本文介绍的是seajs模块之间依赖的加载以及模块的执行,下面话不多说直接来看详细的介绍。
入口方法
每个程序都有个入口方法,类似于c的main
函数,seajs也不例外。系列一的demo在首页使用了seajs.use()
,这便是入口方法。入口方法可以接受2个参数,第一个参数为模块名称,第二个为回调函数。入口方法定义了一个新的模块,这个新定义的模块依赖入参提供的模块。然后设置新模块的回调函数,用以在loaded
状态之后调用。该回调函数主要是执行所有依赖模块的工厂函数,最后在执行入口方法提供的回调。
// Public API // 入口地址 seajs.use = function(ids, callback) { Module.preload(function() { Module.use(ids, callback, data.cwd + "_use_" + cid()) }) return seajs } // Load preload modules before all other modules Module.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 module Module.use = function (ids, callback, uri) { var mod = Module.get(uri, isArray(ids) "color: #ff0000">加载依赖之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 onload 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 onload 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 "htmlcode">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) "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指针引用偏移的危险,做了积极的预防,这种精神值得学习。
对于seajs,前前后后花了不下一个星期来阅读源码,从刚开始的一知半解到如今的拜服,我真切的领会到了设计思想的重要性。之前我不怎么重视实现的技巧性,认为能够实现,不出bug,健壮性良好即可,但是现在我意识到错了,尤其是在load依赖模块那部分实现中,技巧性十足。以上就是本文的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
稳了!魔兽国服回归的3条重磅消息!官宣时间再确认!
昨天有一位朋友在大神群里分享,自己亚服账号被封号之后居然弹出了国服的封号信息对话框。
这里面让他访问的是一个国服的战网网址,com.cn和后面的zh都非常明白地表明这就是国服战网。
而他在复制这个网址并且进行登录之后,确实是网易的网址,也就是我们熟悉的停服之后国服发布的暴雪游戏产品运营到期开放退款的说明。这是一件比较奇怪的事情,因为以前都没有出现这样的情况,现在突然提示跳转到国服战网的网址,是不是说明了简体中文客户端已经开始进行更新了呢?
更新日志
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]