標籤:style blog http java 使用 檔案 io for
先看一個seajs的官方example, 以下以seajs.use(‘main‘)為例, 解析載入mod main的過程
//app.htmlseajs.use("main"); //main.jsdefine(function(require) { var Spinning = require(‘./spinning‘); var s = new Spinning(‘#container‘); s.render();}); //spinning.jsdefine(function(require, exports, module) { var $ = require(‘jquery‘); function Spinning(container) { this.container = $(container); this.icons = this.container.children(); this.spinnings = []; } module.exports = Spinning; function random(x) { return Math.random() * x };});module在載入過程中的幾種狀態
Module.STATUS = { // 1 - The `module.uri` is being fetched FETCHING: 1, // 2 - The meta data has been saved to cachedMods SAVED: 2, // 3 - The `module.dependencies` are being loaded LOADING: 3, // 4 - The module are ready to execute LOADED: 4, // 5 - The module is being executed EXECUTING: 5, // 6 - The `module.exports` is available EXECUTED: 6}每個mod在載入過程中會維護一些自身的重要屬性,如
dependencies: 模組的直接依賴
_remain: 預設為當前mod的dependencies的length,用來加鎖,只有當自身直接依賴的所有模組都載入完畢,即狀態為LOADED的時候, _remain變為0,此時觸發當前mod的onload, 通知直接依賴它的模組。
_waiting: 儲存直接依賴它的模組表,這個屬性,使得模組之間建立起依賴關係鏈, 對模組載入完成後的通知機制非常重要。
factory: 為define(id, deps, factory) module定義中的參數
callback: 為使用use時產生的module的特有屬性, 這個在喚醒機制的時候比較有用
圖解seajs.use(‘main‘), 模組的載入過程如下:
執行過程分析:
使用use調用,會自動構建一個module,id為當前文檔中use的次數和use進行的拼接, 如_use_0, 一般它會成為依賴鏈的源頭。
模組載入完成後,先觸發JS檔案內容執行,此時define函數被執行, 完成該module的兩個重要屬性dependencies & _waiting的初始化。
模組載入完成後,觸發onload事件,如當前模組有dependencies,進入並行載入流程。 或者當發現當前模組的dependencies全部載入完畢或者為空白時, 根據_waiting來逐級喚醒,執行module的callback,由於只有使用use調用的時候才會為mod添加callback屬性,所以這個過程一般會向上索引到依賴鏈的源頭,在上述代碼執行個體中,這個依賴鏈大概可以描述如下:
執行use函數建立的module 會給添加一個自訂的callback函數, 因為通常作為依賴鏈的源頭, 需要從它開始計算和它有直接關係的dependencies的exports, 計算過程相當於將依賴的module的factory函數依次執行一遍,如在執行中發現代碼中有requrie的情況,如main.js 檔案中的 var Spinning = require(‘./spinning‘),再執行一下require 的module。
代碼如下:
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}
計算好exports後, 會用exports作為參數在調用當前module原來的callback, module執行完畢。
和requirejs的最大區別
最大的區別就是seajs是模組載入和模組執行分離, 而requirejs是載入完畢後,會提前計算好module的exports; 在factory 的FunctionBody中的require(‘xxx‘), 只是直接擷取計算好的module xxx 的 exports 而已。
總結
看完seajs和requirejs的模組載入過程, 對指令碼動態載入有了更深入的理解, 在代碼的設計上, 通過使用_waiting來維護一個依賴關係鏈,執行的時候依次向上回溯,找到有callback屬性的mod開始執行, 就讓本來複雜的依賴關係管理變得簡單, 將代碼載入和執行分離有好處也會壞處,這個網上也有很多討論,就不展開描述, 總之還是覺得值得一看吧。
end