CMD和seaJS

前面的話

  CMD(Common Module Definition)表示通用模塊定義,該規範是國內發展出來的,由阿里的玉伯提出。就像AMD有個requireJS,CMD有個瀏覽器的實現SeaJS,SeaJS和requireJS同樣,都是javascript的模塊化解決方案。本文將詳細介紹CMD和seaJSjavascript

 

CMD

  在Sea.js中,全部JavaScript模塊都遵循CMD(Common Module Definition)模塊定義規範。該規範明確了模塊的基本書寫格式和基本交互規則html

  AMD規範簡單到只有一個API,即define函數java

define([module-name?], [array-of-dependencies?], [module-factory-or-object]);

  module-name: 模塊標識,能夠省略node

  array-of-dependencies: 所依賴的模塊,能夠省略jquery

  module-factory-or-object: 模塊的實現,或者一個JavaScript對象git

  CMD規範也與之相似,只不過第三個參數factory的實現方式不一樣。在CMD規範中,一個模塊就是一個文件。代碼的書寫格式以下github

define(id?, deps?, factory)

  與AMD規範相似,define是一個全局函數,用來定義模塊。字符串 id 表示模塊標識,數組 deps 是模塊依賴。這兩個參數能夠省略,一般由構建工具自動生成數組

  一般地,define()方法的第三個參數factory是一個函數,表示是模塊的構造方法。執行該構造方法,能夠獲得模塊向外提供的接口。factory 方法在執行時,默認會傳入三個參數:requireexports 和 module瀏覽器

  [注意]factory()方法的參數若是不須要,能夠省略,但不能夠修改,如修改成'a'、'b'、'c',也不能夠改變其參數的順序。在函數內部,也不能對參數名從新賦值,如'var a = require; '網絡

define(function(require, exports, module) {

  // 模塊代碼

});

【require】

  require 是 factory 函數的第一個參數。require 是一個方法,接受 模塊標識 做爲惟一參數,用來獲取其餘模塊提供的接口。通俗地說,經過require()方法來調用其餘模塊的屬性或方法

複製代碼
define(function(require, exports, module) {

  // 獲取模塊 a 的接口
  var a = require('./a');

  // 調用模塊 a 的方法
  a.doSomething();

});
複製代碼

  這個require()方法的實現和功能都特別相似於CommonJS中的require()方法。或許,有人會有疑惑,require()不是一個同步方法嗎?在CommonJS中是的,在seaJS中也能夠這麼說,但並不完整。更合理的說法應該是,模塊內的同步加載,實際表現爲對模塊a進行預下載

  例以下面的代碼,即便不點擊頁面,a.js也會預先下載。點擊頁面後,控制檯依次輸出'a'和'a.test'

複製代碼
// main.js
define(function(require, exports, module){
    document.onclick = function(){
        var a = require('js/a');
        a.test();
    }    
});
define(function(require, exports, module){
    console.log('a');
    exports.test = function(){
        console.log('a.test');
    }
})
複製代碼

  能不能執行時再下載呢?相似於懶加載。有的,使用require.async()方法。require.async 方法用來在模塊內部異步加載模塊,並在加載完成後執行指定回調

複製代碼
// main.js
define(function(require, exports, module){
    document.onclick = function(){
        require.async('./a',function(a){
            a.test();
        });
    }    
});
//a.js
define(function(require, exports, module){
    console.log('a');
    exports.test = function(){
        console.log('a.test');
    }
})
複製代碼

【exports】

  exports 是一個對象,用來向外提供模塊接口。與CommonJS的exports功能相似

複製代碼
define(function(require, exports) {

  // 對外提供 foo 屬性
  exports.foo = 'bar';

  // 對外提供 doSomething 方法
  exports.doSomething = function() {};

});
複製代碼

  除了給 exports 對象增長成員,還可使用 return 直接向外提供接口,這種方式與requireJS的方式相似

複製代碼
define(function(require) {

  // 經過 return 直接提供接口
  return {
    foo: 'bar',
    doSomething: function() {}
  };

});
複製代碼

  若是 return 語句是模塊中的惟一代碼,還可簡化爲

define({
  foo: 'bar',
  doSomething: function() {}
});

【module】

  module 是一個對象,上面存儲了與當前模塊相關聯的一些屬性和方法

// main.js
define(['./a'],function(require, exports, module){
    console.log(module);
})

  module.uri表示根據模塊系統的路徑解析規則獲得的模塊絕對路徑

  module.id是模塊的惟一標識,通常狀況下沒有在define中手寫id參數時,module.id的值就是module.uri,二者徹底相同

  module.dependencies是一個數組,表示當前模塊的依賴

  module.exports是當前模塊對外提供的接口。傳給factory構造方法的exports參數是module.exports對象的一個引用。只經過exports參數來提供接口,有時沒法知足開發者的全部需求。 好比當模塊的接口是某個類的實例時,須要經過module.exports來實現

  [注意]對module.exports的賦值須要同步執行,不能放在回調函數裏。下面這樣是不行的

複製代碼
define(function(require, exports, module) {
  // 錯誤用法
  setTimeout(function() {
    module.exports = { a: "hello" };
  }, 0);
});
複製代碼

 

入口

  requireJS經過data-main來設置入口,而seaJS則經過sea.use()來設置。sea.js 在下載完成後,會自動加載入口模塊

seajs.use(id, callback?)

  [注意]callback參數可選,省略時,表示無需回調

<script src="sea.js"></script>
<script>
  seajs.use('js/main');
</script>

  加載單個依賴,運行如下代碼後,控制檯輸出'test'

複製代碼
//index.html
<script src="sea.js"></script>
<script>
    seajs.config({
        base: 'js'
    });
    seajs.use("main",function(a){
        a.test();
    });
</script>

// main.js
define(['./a'],function(require, exports, module){
    return {
        test : function(){
            console.log('test');
        }
    }
})
複製代碼

  加載多個依賴

//併發加載模塊 a 和模塊 b,並在都加載完成時,執行指定回調
seajs.use(['./a', './b'], function(a, b) {
  a.init();
  b.init();
});

【DOMReady】

  seajs.useDOM ready事件沒有任何關係。若是某些操做要確保在DOM ready後執行,須要使用jquery等類庫來保證

seajs.use(['jquery', './main'], function($, main) {
  $(document).ready(function() {
    main.init();
  });
});

【打包】

  引入 sea.js 時,能夠把 sea.js 與其餘文件打包在一塊兒,可提早合併好,或利用 combo 服務動態合併。不管哪種方式,爲了讓 sea.js 內部能快速獲取到自身路徑,推薦手動加上 id 屬性

<script src="path/to/sea.js" id="seajsnode"></script>

  加上 seajsnode 值,可讓 sea.js 直接獲取到自身路徑,而不須要經過其餘機制去自動獲取。這對性能和穩定性會有必定提高,推薦默認都加上

 

配置

【路徑】

  若是不配置路徑,在requireJS中,默認路徑是data-main的所處目錄,好比data-main='js/main',則所處路徑是'js'目錄下

  而seaJS則不一樣,它的默認路徑是seaJS文件的所處目錄,好比seaJS文件所處路徑是'demo'目錄下,進行以下入口設置後

seajs.use('js/main');

  說明main.js的目錄爲'demo/js/main.js'。若是main.js依賴於a.js,且a.js與main.js處於同一目錄下,則如下兩種寫法都正確

  一、'demo' + 'js/a' = 'demo/js/a.js'

// main.js
define(['js/a'],function(require, exports, module){
    
})

  二、'./'表示當前目錄,即'demo/js',因此 './a' = 'demo/js/a.js'

// main.js
define(['./a'],function(require, exports, module){
    
})

  在requireJS中使用baseUrl來配置基礎路徑,而在seaJS中使用base。進行以下配置後,真實路徑爲 'demo' + 'js' + 'main' = 'demo/js/main.js'

複製代碼
<script src="sea.js"></script>
<script>
    seajs.config({
        base: 'js'
    });
    seajs.use("main");
</script>
複製代碼

【別名】

  當模塊標識很長時,可使用 alias 來簡化

複製代碼
seajs.config({
  alias: {
    'jquery': 'jquery/jquery/1.10.1/jquery',
    'app/biz': 'http://path/to/app/biz.js',
  }
});
複製代碼

【目錄】

  當目錄比較深,或須要跨目錄調用模塊時,可使用 paths 來簡化書寫

複製代碼
seajs.config({
  paths: {
    'gallery': 'https://a.alipayobjects.com/gallery',
    'app': 'path/to/app',
  }
});
複製代碼

 

與AMD區別

  AMD 是 RequireJS 在推廣過程當中對模塊定義的規範化產出,CMD 是 SeaJS 在推廣過程當中對模塊定義的規範化產出。這些規範的實現都能達成瀏覽器端模塊化開發的目的

  AMD與CMD主要有如下兩點區別

  一、所依賴模塊的執行時機

  對於依賴的模塊,AMD是提早執行,CMD是延遲執行

  AMD在加載模塊完成後就會執行該模塊,全部模塊都加載執行完後會進入require的回調函數,執行主邏輯,這樣的效果就是依賴模塊的執行順序和書寫順序不必定一致,看網絡速度,哪一個先下載下來,哪一個先執行,可是主邏輯必定在全部依賴加載完成後才執行。不過,新版本的RequireJS也能夠延遲執行

  CMD加載完某個依賴模塊後並不執行,只是下載而已,在全部依賴模塊加載完成後進入主邏輯,遇到require語句的時候才執行對應的模塊,這樣模塊的執行順序和書寫順序是徹底一致的。若是使用require.async()方法,能夠實現模塊的懶加載,即不執行不下載

  二、CMD推崇依賴就近,AMD推崇依賴前置

複製代碼
// CMD
define(function(require, exports, module) { 
    var a = require('./a')
     a.doSomething()  
    // 此處略去 100 行   
    var b = require('./b') // 依賴能夠就近書寫  
    b.doSomething()   
    // ... 
})
複製代碼
複製代碼
// AMD
define(['./a', './b'], function(a, b) {  // 依賴必須一開始就寫好
    a.doSomething()    
    // 此處略去 100 行    
    b.doSomething()    
    ...
})
複製代碼

  固然,AMD也支持CMD的寫法,同時還支持將require做爲依賴項傳遞

 

最後

  CommonJSrequireJSseaJS這三種模塊化方案,並無高低之分。隨着各個方案的不斷升級,語言方面相互借鑑,使用差別逐漸變小。以上三種庫級別的模塊化方案,須要引入額外的庫,且所遵循的規範並非標準組織制定的,權威性不足

  隨着ES6在語言層面上開始支持模塊化,ES6的模塊化寫法纔是將來的模塊化標準

相關文章
相關標籤/搜索