seajs 源碼閱讀筆記

代碼概覽

src目錄文件列表以下:
代碼以模塊化的方式來組織,構建的時候會合併爲一個js文件(sea.js 或 sea-debug.js),其中,intro.js和 outro.js 分別是這個js文件的頭部和尾部。
若是習慣看一個文件的代碼,能夠直接閱讀dist目錄下的 sea-debug.js , 這個是 全部模塊合併後的代碼 。
sea.js 記錄了當前的版本,「@VERSION」在構建的時候應該會被替換爲具體的版本號。
util 開頭的文件是一些工具方法,好比 路徑的轉換、語法的加強、事件等;
util-lang.js 實現了 isObject 、isString、isArray、isFunction isUndefined 等用於判斷對象類型的方法。
util-events.js 實現了事件機制,爲 seajs 對象添加了三個方法:on、off、emit;  on 函數用於添加一個事件監聽函數,off 用於移除監聽函數,emit用於觸發一個事件。 全部添加的事件監聽函數,都被存儲在一個 events 二維數組中,當一個事件被emit後,會在events找到爲該事件添加的全部監聽函數,而後循環執行。
util-path.js 有一些用於處理文件路徑相關的方法,好比 id2Url 能夠把一個模塊的id轉換爲一個完整的url;seajs.resolve(Module.resolve) 方法 其實就指向這裏的 id2Url。
util-request.js 實現了 seajs.request 方法,用於根據url 加載js腳本;此方法會判斷當前瀏覽器是否支持 webWorker ,若是支持,則使用importScripts的方式加載腳本,若是不支持web worker,則採用建立 script 節點的方式實現js腳本的加載,爲了防止在IE中的內存泄露,在腳本加載完onload後,會及時移除該節點。
util-deps.js 只有一個方法 parseDependencies,該方法能夠根據一坨js代碼,解析出這些js代碼中依賴的其餘模塊,也就是require的那些模塊。主要是爲了解決,在define方法中,第二個參數沒有指明依賴的模塊,但又在回調方法中使用require了其餘模塊的狀況。
config.js 實現了 seajs.config 方法,用於保存一些配置信息,模塊別名、url路由規則、調試開關;保存完後,會emit一個config事件。
module.js 是最核心的一個文件,全部模塊化相關的代碼都在這個文件中,好比 define方法、require,use,模塊的加載、初始化、解析、執行。

核心代碼

gloable.define 最終調用的是Module.define ,該方法會從cachedMods 中根據id找到相應的Module對象(若是不存在,則建立一個並保存到cachedMods中),使用define參數中的 deps(依賴模塊id)、factory(第三個參數)來初始化相應的本模塊。若是deps參數爲空,則define方法會調用parseDependencies(util-deps.js),解析factory中經過require引用的其餘模塊,並把解析的結果賦值給本模塊。 
define 涉及到的方法主要有 Module.save、Module.get,調用層次以下:
 define
       |-- Module.save : 建立 module , 並緩存在 cachedMods 中,而且,若是新建立的module 狀態小於SAVED,則設置爲 SAVED

       `-- Module.get :  根據 id 從 cachedMods 中獲取 Module 對象,若是沒有則建立 Module,並緩存在 cachedMods 中。javascript

seajs.use(Module.use) 是模塊的入口,該方法會從服務端加載直接依賴的模塊,以及依賴模塊的依賴模塊...也就是全部間接依賴的模塊;當全部模塊加載完以後,瀏覽器會自動執行這些模塊的define方法,在define中會建立這些模塊對應的Module對象,並保存到cachedMods中;全部模塊觸發onload事件(發生在define執行以後)以後,seajs會執行直接依賴模塊的exec方法;exec方法會構造require、exports、module,做爲參數傳遞給本模塊factory方法。
use 涉及到的方法主要有,load resolve get pass fetch,調用層次以下:
seajs.use
    |-- Module.use 
        |-- Module.get :  new一個Module,並把new的Module緩存到cachedMods變量中。狀態爲 -1
        |-- Module.prototype.load: 先把當前模塊的依賴模塊存儲到cachedMod中,而後依次從服務端加載(Fetch)依賴模塊,
                                                加載完而且瀏覽器執行完define方法後,onload事件中會調用依賴模塊的load方法,從而達到遞歸加載直接和間接依賴的目的。
            |-- Module.prototype.resolve :處理依賴模塊的url,把依賴模塊的id轉換爲url並返回。
            |-- Module.get :new依賴模塊
            |-- Module.prototype.pass: 把入口模塊(通常是經過seajs.using)傳遞給依賴模塊,最終傳遞給最後一個被依賴的模塊;當最後一個被依賴的模塊加載完以後,會經過onload方法,調用入口模塊的callback,從而執行Module.exec方法。
            |-- Module.prototype.fetch : 遍歷當前模塊所依賴的模塊,把依賴模塊從服務端加載出來,加載完後,瀏覽器會當即執行模塊的define方法,此時依賴模塊的狀態爲SAVED。
            |--     seajs.request :用webWorker或建立script標籤的方式加載javascript腳本。
            |--     gloable.define 腳本加載完後,由瀏覽器調用。
        `--Module.prototype.exec : 在onload方法中,會執行此方法。 修改狀態爲 EXECUTING,構造require、exports、module,執行factory方法(difine中的回調方法)。
 
require 方法會根據 id 獲取module對象,而後調用 該module的 exec 方法,調用層次:
 require : 根據 id 獲取module對象,而後調用 該module的 exec 方法
        |-- Module.get  從cachedMod中獲取 或建立對象。
        `-- Module.prototype.exec : 修改狀態爲 EXECUTING,構造require、exports、module,執行factory方法(difine中的回調方法)。
 
在執行以上方法的時候,module會有一個status屬性標記着當前模塊的狀態,一個模塊的生命週期以下:
var STATUS = Module.STATUS = {
  // 1 - 正在執行 fetch 方法
  FETCHING: 1,
  // 2 - 執行完了 saved 方法
  SAVED: 2,
  // 3 - 正在執行完load方法
  LOADING: 3,
  // 4 - 已經load完
  LOADED: 4,
  // 5 - 正在執行exec方法
  EXECUTING: 5,
  // 6 - exec方法執行完畢
  EXECUTED: 6,
  // 7 - 出現錯誤,通常是404。
  ERROR: 7
}
 
define、require、exec等方法在執行時,都會觸發(emit)相應的事件,以便經過插件的方式實現除seajs核心之外的功能,如 seajs-combo 等。
相關文章
相關標籤/搜索