在 Sea.js 中,所有 JavaScript 模塊都遵循 CMD(Common Module Definition) 模塊定義規范。該規范明確了模塊的基本書寫格式和基本交互規則。
在 CMD 規范中,一個模塊就是一個文件。代碼的書寫格式如下:
define(factory);
define Function
define
是一個全局函數,用來定義模塊。
define 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)
define
也可以接受兩個以上參數。字符串 id
表示模塊標識,數組 deps
是模塊依賴。比如:
define('hello', ['jquery'], function(require, exports, module) { // 模塊代碼 });id
和deps
參數可以省略。省略時,可以通過構建工具自動生成。
注意:帶id
和deps
參數的define
用法不屬於 CMD 規范,而屬於 Modules/Transport 規范。
define.cmd Object
一個空對象,可用來判定當前頁面是否有 CMD 模塊加載器:
if (typeof define === "function" && define.cmd) { // 有 Sea.js 等 CMD 模塊加載器存在 }
require Function
require
是 factory
函數的第一個參數。
require require(id)
require
是一個方法,接受 模塊標識 作為唯一參數,用來獲取其他模塊提供的接口。
define(function(require, exports) { // 獲取模塊 a 的接口 var a = require('./a'); // 調用模塊 a 的方法 a.doSomething(); });
注意:在開發時,require
的書寫需要遵循一些 簡單約定。
require.async require.async(id, callback?)
require.async
方法用來在模塊內部異步加載模塊,並在加載完成后執行指定回調。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
是同步往下執行,require.async
則是異步回調執行。require.async
一般用來加載可延遲異步加載的模塊。
require.resolve require.resolve(id)
使用模塊系統內部的路徑解析機制來解析並返回模塊路徑。該函數不會加載模塊,只返回解析后的絕對路徑。
define(function(require, exports) { console.log(require.resolve('./b')); // ==> http://example.com/path/to/b.js });
這可以用來獲取模塊路徑,一般用在插件環境或需動態拼接模塊路徑的場景下。
exports Object
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
語句是模塊中的唯一代碼,還可簡化為:
define({ foo: 'bar', doSomething: function() {} });
上面這種格式特別適合定義 JSONP 模塊。
特別注意:下面這種寫法是錯誤的!
define(function(require, exports) { // 錯誤用法!!! exports = { foo: 'bar', doSomething: function() {} }; });
正確的寫法是用 return
或者給 module.exports
賦值:
define(function(require, exports, module) { // 正確寫法 module.exports = { foo: 'bar', doSomething: function() {} }; });
提示:exports
僅僅是 module.exports
的一個引用。在 factory
內部給 exports
重新賦值時,並不會改變 module.exports
的值。因此給 exports
賦值是無效的,不能用來更改模塊接口。
module Object
module
是一個對象,上面存儲了與當前模塊相關聯的一些屬性和方法。
module.id String
模塊的唯一標識。
define('id', [], function(require, exports, module) { // 模塊代碼 });
上面代碼中,define
的第一個參數就是模塊標識。
module.uri String
根據模塊系統的路徑解析規則得到的模塊絕對路徑。
define(function(require, exports, module) { console.log(module.uri); // ==> http://example.com/path/to/this/file.js });
一般情況下(沒有在 define
中手寫 id
參數時),module.id
的值就是 module.uri
,兩者完全相同。
module.dependencies Array
dependencies
是一個數組,表示當前模塊的依賴。
module.exports Object
當前模塊對外提供的接口。
傳給 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 里有調用到上面的 x.js:
// y.js define(function(require, exports, module) { var x = require('./x'); // 無法立刻得到模塊 x 的屬性 a console.log(x.a); // undefined });
小結
這就是 CMD 模塊定義規范的所有內容。經常使用的 API 只有 define
, require
, require.async
, exports
,module.exports
這五個。其他 API 有個印象就好,在需要時再來查文檔,不用刻意去記。
與 RequireJS 的 AMD 規范相比,CMD 規范盡量保持簡單,並與 CommonJS 和 Node.js 的 Modules 規范保持了很大的兼容性。通過 CMD 規范書寫的模塊,可以很容易在 Node.js 中運行,后續會介紹。