隨着瀏覽器功能愈來愈完善,前端已經不只僅是切圖作網站,前端在某些方面已經媲美桌面應用。愈來愈龐大的前端項目,愈來愈複雜的代碼,前端開發者們對於模塊化的需求空前強烈。後來node出現了,跟隨node出現的還有commonjs,這是一種js模塊化解決方案,像Node.js主要用於服務器的編程,加載的模塊文件通常都已經存在本地硬盤,因此加載起來比較快,不用考慮異步加載的方式,CommonJS 加載模塊是同步的,因此只有加載完成才能執行後面的操做。可是瀏覽器環境不一樣於Node,瀏覽器中獲取一個資源必需要發送http請求,從服務器端獲取,採用同步模式必然會阻塞瀏覽器進程出現假死現象。在這方面dojo曾經作了偉大嘗試,早期dojo即是採用xhr+eval的方式,結果可想而知,阻塞現象是必然的。後來出現無阻塞加載腳本方式在開發中普遍應用,在此基礎結合commonjs規範,前端模塊化迎來了兩種方案:AMD、CMD.javascript
借用三藏法師一句話:人是人他媽生的,妖是妖他媽生的。此話雖不雅,但用這裏卻頗爲貼切。AMD 是 RequireJS 在推廣過程當中對模塊定義的規範化產出,CMD是SeaJS 在推廣過程當中被普遍認知。RequireJs出自dojo加載器的做者James Burke,SeaJs出自國內前端大師玉伯。兩者的區別,玉伯在12年如是說:html
RequireJS 和 SeaJS 都是很不錯的模塊加載器,二者區別以下: 1. 二者定位有差別。RequireJS 想成爲瀏覽器端的模塊加載器,同時也想成爲 Rhino / Node 等環境的模塊加載器。SeaJS 則專一於 Web 瀏覽器端,同時經過 Node 擴展的方式能夠很方便跑在 Node 服務器端 2. 二者遵循的標準有差別。RequireJS 遵循的是 AMD(異步模塊定義)規範,SeaJS 遵循的是 CMD (通用模塊定義)規範。規範的不一樣,致使了二者 API 的不一樣。SeaJS 更簡潔優雅,更貼近 CommonJS Modules/1.1 和 Node Modules 規範。 3. 二者社區理念有差別。RequireJS 在嘗試讓第三方類庫修改自身來支持 RequireJS,目前只有少數社區採納。SeaJS 不強推,而採用自主封裝的方式來「海納百川」,目前已有較成熟的封裝策略。 4. 二者代碼質量有差別。RequireJS 是沒有明顯的 bug,SeaJS 是明顯沒有 bug。 5. 二者對調試等的支持有差別。SeaJS 經過插件,能夠實現 Fiddler 中自動映射的功能,還能夠實現自動 combo 等功能,很是方便便捷。RequireJS 無這方面的支持。 6. 二者的插件機制有差別。RequireJS 採起的是在源碼中預留接口的形式,源碼中留有爲插件而寫的代碼。SeaJS 採起的插件機制則與 Node 的方式一致:開放自身,讓插件開發者可直接訪問或修改,從而很是靈活,能夠實現各類類型的插件。.
關於兩者的區別,前人之述備矣:前端
讓咱們再聊聊瀏覽器資源加載優化node
而在本文,咱們僅從代碼愛好者的角度來一窺兩者API、模塊管理、加載、執行的異同。瀏覽器
對比AMD與CMD規範,兩者最大的區別在於依賴模塊的執行時期,CMD規範中明確要求延遲執行(Execution must be lazy.)。這一點從兩者在模塊的定義方法define的函數簽名上能夠看出:服務器
define(id?, dependencies?, factory);
define(["./a", "./b"], function(a, b) { //BEGIN a.doSomething(); b.doSomething(); });
CMD中模塊以下定義:
define(function(require, exports, module) { // The module code goes here });
define(function(require, exports, module) { //BEGIN require("./a").doSomething(); require("./b").doSomething(); });
須要提一下的是兩者對待依賴模塊的加載是一致的,在factory執行時,依賴模塊都已被加載。從代碼上來看,AMD中在BEGIN處a、b的factory都是執行過的;而CMD中雖然a、b模塊在BEGIN已被加載,但還沒有執行,須要調用require執行依賴模塊。這就是CMD中着重強調的延遲執行。若是這個例子不明顯的話,咱們來看一下條件依賴:
AMD:
define(["./a", "./b"], function(a, b) { //BEGIN if (true) { a.doSomething(); } else { b.doSomething(); } //END });
CMD:
define(function(require) { // BEGIN if(some_condition) { require('./a').doSomething(); } else { require('./b').soSomething(); } // END });
條件依賴意思是咱們根據條件使用依賴項,在AMD中BEGIN位置處a、b模塊都須要被執行一次。CMD中BEGIN處a、b都沒有被執行,在END處,a、b只有一個被實際執行過。
那麼問題來了,javascript做爲腳本語言,代碼確定是順序執行的,做爲AMD與CMD的實現者,requireJs與seaJs是如何知道須要加載的全部文件呢?又是如何作到異步加載?對於seajs,factory中代碼確定是順序執行的,可是這必須致使require時的阻塞加載,而她又是如何保證異步加載的?
每個卓越的思想都有一份樸實的代碼實現。因此不管AMD與CMD都要面臨如下幾個問題:
scriptElement= document.createElement('script'); scriptElement.src = moduleUrl; scriptElement.async = true; scriptElement.onload = function(){.........}; document.head.appendChild(scriptElement);
五、模塊加載完畢後,獲取依賴項(amd、cmd區別),改變模塊status,由statuschange後,檢測全部模塊的依賴項。
因爲requirejs與seajs遵循規範不一樣,requirejs在define函數中能夠很容易得到當前模塊依賴項。而seajs中不須要依賴聲明,因此必須作一些特殊處理纔可否得到依賴項。方法將factory做toString處理,而後用正則匹配出其中的依賴項,好比出現require(./a),則檢測到須要依賴a模塊。
同時知足非阻塞和順序執行就須要須要對代碼進行一些預處理,這是因爲CMD規範和瀏覽器環境特色所決定的。
六、若是模塊的依賴項徹底加載完畢(amd中須要執行完畢,cmd中只須要文件加載完畢,注意這時候的factory還沒有執行,當使用require請求該模塊時,factory纔會執行,因此在性能上seajs遜於requirejs),執行主模塊的factory函數;不然進入步驟3.
最後,不管requireJs仍是seaJs都已被普遍應用於web開發中,實際選取時應根據如下幾方面綜合平衡選取: