javascript模塊化編程的cmd規范(sea.js)


CMD(Common Module Definition,通用模塊定義)是一種模塊定義規范,規范中明確了模塊的基本書寫格式和基本交互規則。SeaJS就是遵循的這個規范。

define函數

在CMD規范中,一個模塊就是一個文件,模塊的區分通過define關鍵字來定義,最基本的格式是:

define(factory); // 這里的define是一個全局函數,factory可以是函數、對象或字符串

define接受factory參數,factory可以是一個函數,也可以是一個對象或字符串。

當factory為對象、字符串時,表示模塊的接口就是該對象、字符串。

比如可以如下定義一個JSON數據模塊:

define({ "foo": "bar" });

也可以通過字符串定義模板模塊:

define('I am a template. My name is {{name}}.');

當factory為函數時,表示是模塊的構造方法。執行該構造方法,可以得到模塊向外提供的接口。

當factory方法在執行時,默認會傳入三個參數:require、exports和module:

define(function(require, exports, module) {
    // 模塊代碼
});

define也可以接受兩個以上的參數,進階的格式是:

define(id, deps, factory); // id是模塊標識字符串,deps是模塊依賴數組

參數中,字符串id表示模塊標注,數組deps表示模塊依賴。

define('helloworld', ['jquery'], function(require, exports, module) {
    // 模塊代碼
});

id和deps參數是可以省略的,省略時可以通過構建工具自動生成。注意的是帶id和deps參數的define用法不屬於CMD規范,而屬於Modules/Transport規范。

define函數帶有成員屬性cmd,是一個空對象,可用來判斷當前頁面是否有CMD模塊加載器。

if (typeof define === "function" && define.cmd) {
    // 有 Sea.js 等 CMD 模塊加載器存在時的邏輯代碼
}

require函數(factory函數的參數之一)

require是factory函數的第一個參數,它有下列的用法:

require(id),通過接受模塊標識作為唯一參數,用來獲取其他模塊提供的接口。

define(function(require, exports) {
    // 獲取模塊 a 的接口
    var a = require('./helloworlda');

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

require.async(id, callback),用來在模塊內部加載模塊,並在加載完成后執行指定回調。callback參數可選。

define(function(require, exports, module) {
    // 異步加載一個模塊,在加載完成時,執行回調
    require.async('./b', function(b) {
        b.doSomething();
    });

    // 異步加載多個模塊,在加載完成時,執行回調
    require.async(['./c', './d'], function(c, d) {
        c.doSomething();
        d.doSomething();
    });
});

與上一個用法不同的是,前者是同步往下執行,后者是異步回調執行,一般用來加載可延遲異步加載的模塊。

require.resolve(id),使用模塊系統內部的路徑解析機制來解析並返回模塊路徑,該函數並不會加載模塊,只返回解析后的絕對路徑。

define(function(require, exports) {
    console.log(require.resolve('./b'));// http://path/b.js
});

這樣就可以獲取模塊的路徑,一般用在插件環境或需要動態拼接模塊路徑的場景下。

exports對象(factory函數的參數之一)

exports是一個對象,用來向外提供模塊接口。

define(function(require, exports) {
    // 對外提供 foo 屬性
    exports.foo = 'bar';

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

除了給exports對象增加成員,還可以使用return直接向外提供接口。

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

如果return語句是模塊中的唯一代碼,還可以簡化為(factory為對象或字符串):

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

上面這種格式特別適合定義一個JSONP模塊。

要注意的是,exports僅僅是module.exports的一個引用,在factory內部給exports重新賦值時,並不會改變module.exports的值,因此給exports賦值是無效的,不能用來更改模塊接口。比如以下的寫法就是錯誤的:

define(function(require, exports) {
    // 錯誤用法!!!
    exports = { // 應該改為module.exports = {}
        foo: 'bar',
        doSomething: function() {}
    };
});

module對象(factory函數的參數之一)

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

module.id,模塊的唯一標識。

define('yanggb', [], function(require, exports, module) {
    // 模塊代碼
    console.log(module.id); // yanggb
});

define函數的第一個參數就是模塊唯一標識,用module取得時候就能得到yanggb。

module.uri,模塊的絕對路徑,根據模塊系統的路徑解析規則得到。

define(function(require, exports, module) {
    // 模塊代碼
    console.log(module.uri); // http://path/file.js
});

一般情況下(沒有在define函數中自定義id參數),module.id的值就是module.uri的值。

module.dependencies,模塊的依賴,是一個數組。

module.exports,模塊對外提供的接口。

傳給factory函數的exports參數是module.exports對象的一個引用。只通過exports參數來提供接口,有時無法滿足開發者的所有需求。例如當模塊的接口是某個類的實例時,就需要通過module.exports來實現:

define(function(require, exports, module) {
    // exports 是 module.exports 的一個引用
    console.log(module.exports === exports); // true

    // 重新給 module.exports 賦值
    module.exports = new SomeClass();

    // exports 不再等於 module.exports
    console.log(module.exports === exports); // false
});

要注意的是,對module.exports的賦值必須要同步執行,不能放到回調函數里(異步)。

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

// y.js,在 y.js 里有調用到上面的 x.js:
define(function(require, exports, module) {
    var x = require('./x');

    // 無法立刻得到模塊 x 的屬性 a (有延遲)
    console.log(x.a); // undefined
});

總結

這里學習的順序就是:define函數 -> factory函數(define函數的參數) -> require函數、exports對象和modules對象(factory函數的參數)。其實用法也比較簡單,對開發很有幫助,有助於開發思路的整理和后期代碼的維護。優點也就是模塊化編程的優點。

 

"我唱的不夠動人你別皺眉。"


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM