搜尋
首頁php教程PHP开发seajs模組之間依賴的載入以及模組的執行

本文介紹的是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) ? 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&#39;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函數定義如下:

載入所有依賴,當依賴載入完畢,執行onRequest回調,向上冒泡,載入依賴的依賴,直至沒有依賴模組。

當最上層的依賴已沒有依賴模組時,執行onload函數,在函數體內設定狀態為loaded,執行mod.callback,並檢查並設定模組的_waitings屬性,判斷下層模組是否仍有依賴,若沒有則執行下層模組的mod.callback,這一依次回溯,最終將會執行透過seajs.use建立的匿名模組的mod.callback。

例證

透過一個簡單的例子,論證上述過程:

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

   

透過除錯工具,可看出執行onload的次序:

透過除錯工具,可看出執行onload的次序:

透過除錯工具,可看出執行onload的次序:

,也就是該模組並未執行.確實,也沒有給匿名模組定義工廠函數,無法執行.

模組執行之exec

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

tst.html
 
<script>
  seajs.use(&#39;./b&#39;);
</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));
})

   

require函數取得模組並執行模組的工廠函數,取得傳回值。 require函數的resolve方法則是取得對應模組名的絕對url,require函數的async方法非同步載入依賴並執行回呼。對於工廠方法的回傳值,如果工廠方法為對象,則這就是exports的值;or工廠方法有回傳值,則為exports的值;or module.exports的值為exports的值。當可以取得到exports值時,設定狀態為executed。

值得注意的一點:當想要透過給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
}

   

是不成功的.我們透過執行上述方法來判斷最終導出exports的值.

是不成功的.我們透過執行上述方法來判斷最終導出exports的值。 ,其次,mod.exports為undefined,最終導出的exports為undefined。為什麼會出現這種情況呢?是因為js中引用賦值所造成的。 js的賦值策略是“按共享傳遞”,雖說初始時exports === module.exports,但是當給exports賦一個對象時,此時exports指向該對象,module.exports卻仍未初始化,為undefined,因此會出錯。

正確的寫法為

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

   

總結

可以說,seajs的核心模組的實現已講解完細微處的巧妙考慮,見識了許多編碼技巧,領略了回調模式的考量,以及畢細細處的巧妙考量。程式碼的每一處都考慮到了記憶體洩漏和this指針引用偏移的危險,做了積極的預防,這種精神值得學習。


陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前By尊渡假赌尊渡假赌尊渡假赌
威爾R.E.P.O.有交叉遊戲嗎?
1 個月前By尊渡假赌尊渡假赌尊渡假赌

熱工具

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

WebStorm Mac版

WebStorm Mac版

好用的JavaScript開發工具

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強大的PHP整合開發環境

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器