国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

seajs模塊之間依賴的加載以及模塊的執行

2019-11-20 08:41:51
字體:
來源:轉載
供稿:網友

本文介紹的是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 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 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 ?  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.jsdefine(function(require,exports,module){ exports.add = function(a,b){  return a+b; }})------------------------------------b.jsdefine(function(require,exports,module){ var a = require("./a"); console.log(a.add(3,5));})

通過調試工具,可以看出執行onload的次序:

最后可看出,匿名模塊的狀態碼為4,也就是該模塊并未執行.確實,也沒有給匿名模塊定義工廠函數,無法執行.

模塊執行之exec

模塊執行是在seajs.use中定義的mod.callback中調用的,依次調用所有依賴的exec方法,執行程序邏輯。exec方法中有commonJS的一些重要關鍵字或者函數,如requireexports等,讓我們一看究竟:

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,最終導出的exportsundefined。為什么會出現這種情況呢?是因為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依賴模塊那部分實現中,技巧性十足。以上就是本文的全部內容了,希望本文的內容對大家的學習或者工作能帶來一定的幫助,如果有疑問大家可以留言交流。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 平果县| 会理县| 定边县| 鲁甸县| 鄯善县| 白城市| 梅河口市| 冕宁县| 博客| 兴义市| 从化市| 旬阳县| 青海省| 黄山市| 西贡区| 石阡县| 休宁县| 淮滨县| 肇庆市| 大安市| 且末县| 招远市| 高清| 阿坝县| 五华县| 通城县| 会昌县| 开原市| 沙坪坝区| 尼勒克县| 大余县| 天津市| 肥西县| 石狮市| 连山| 利川市| 娱乐| 明星| 金平| 睢宁县| 光泽县|