CMD 模組定義規範
在 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 中執行