1 概述jquery
CMD(Common Module Definition)是國內大牛玉伯在開發SeaJS的時候提出來的,屬於CommonJS的一種規範,根據瀏覽器的異步環境作了本身的實現。它和 AMD 很類似,儘可能保持簡單,並與 CommonJS 和 Node.js 的 Modules 規範保持了很大的兼容性。git
2 define方法:定義模塊github
在CMD中,一個模塊就是一個文件,格式爲:define( factory );define 是一個全局函數,用來定義模塊。數組
2.1 factory 函數瀏覽器
define 接受 factory 參數,factory 能夠是一個函數,也能夠是一個對象或字符串。異步
factory 爲對象、字符串時,表示模塊的接口就是該對象、字符串。好比能夠以下定義一個 JSON 數據模塊:async
define({ "foo": "bar" });
也能夠經過字符串定義模板模塊:函數
define('I am a template. My name is {{name}}.');
factory 爲函數時,表示是模塊的構造方法。執行該構造方法,能夠獲得模塊向外提供的接口。factory方法在執行時,默認會傳入三個參數:require、exports 和 module:ui
define(function(require, exports, module) { // 模塊代碼 });
define也能夠接受兩個以上的參數,字符串id爲模塊標識,數組deps爲模塊依賴,格式爲:define( id?, deps?, factory );具體使用以下:this
define('hello', ['jquery'], function(require, exports, module) { // 模塊代碼 });
可是,帶 id 和 deps 參數的 define 用法不屬於 CMD 規範。CMD推崇一個文件一個模塊,因此常常就用文件名做爲模塊id。CMD推崇依賴就近,因此通常不在define的參數中寫依賴,而在factory中寫。
2.2 define.cmd 屬性
defin.cmd屬性是一個空對象,可用來斷定當前頁面是否有 CMD 模塊加載器,其用法跟AMD的denfine.amd類似,寫法以下:
if (typeof define === "function" && define.cmd) { // 有 Sea.js 等 CMD 模塊加載器存在 }
3 require方法:加載模塊
require 是 factory 函數的第一個參數。require 是一個方法,接受 模塊標識 做爲惟一參數,用來獲取其餘模塊提供的接口。
define(function(require, exports) { // 獲取模塊 a 的接口 var a = require('./a'); // 調用模塊 a 的方法 a.doSomething(); });
3.1 require.async (id, callback)
require.async 方法用來在模塊內部異步加載模塊,並在加載完成後執行指定回調。callback 參數可選。
define(function(require, exports, module) { // 異步加載模塊,在加載完成時,執行回調 require.async(['./c', './d'], function(c, d) { c.doSomething(); d.doSomething(); }); });
require 是同步往下執行,require.async 則是異步回調執行。require.async 通常用來加載可延遲異步加載的模塊。
define(function(require, exports, module) { // 異步加載模塊,在加載完成時,執行回調 require.async(['./c', './d'], function(c, d) { c.doSomething(); d.doSomething(); }); });
3.2 require.resolve(id)
使用模塊系統內部的路徑解析機制來解析並返回模塊路徑。該函數不會加載模塊,只返回解析後的絕對路徑。
define(function(require, exports) { console.log(require.resolve('./b')); // ==> http://example.com/path/to/b.js });
這能夠用來獲取模塊路徑,通常用在插件環境或需動態拼接模塊路徑的場景下。
exports 是一個對象,用來向外提供模塊接口。
define(function(require, exports) { // 對外提供 foo 屬性 exports.foo = 'bar'; // 對外提供 doSomething 方法 exports.doSomething = function() {}; });
除了給 exports 對象增長成員,還能夠使用 return 直接向外提供接口。
define(function(require) { // 經過 return 直接提供接口 return { foo: 'bar', doSomething: function() {} }; });
須要特別注的是,不能直接給 exports
賦值,例以下面是錯誤的寫法:
define(function(require, exports) { // 錯誤用法!!! exports = { foo: 'bar', doSomething: function() {} }; });
exports 僅僅是 module.exports 的一個引用。而模塊導出的時候,真正導出的是module.exports,而不是exports。在 factory 內部給 exports 從新賦值時, exports 就再也不指向module.exports 了。所以給 exports 賦值是無效的,正確的寫法是用 return 或者給 module.exports 賦值:
define(function(require, exports, module) { // 正確寫法 module.exports = { foo: 'bar', doSomething: function() {} }; });
module 是一個對象,上面存儲了與當前模塊相關聯的一些屬性和方法。
5.1 module.id
模塊的惟一標識。
define('id', [], function(require, exports, module) { // 模塊代碼 });
上面代碼中,define 的第一個參數就是模塊標識。
5.2 module.uri
根據模塊系統的路徑解析規則獲得的模塊絕對路徑。
define(function(require, exports, module) { console.log(module.uri); // ==> http://example.com/path/to/this/file.js });
通常狀況下(沒有在 define 中手寫 id 參數時),module.id 的值就是 module.uri,二者徹底相同。
5.3 module.dependencies
dependencies 是一個數組,表示當前模塊的依賴。
5.4 module.exports
表示當前模塊對外輸出的接口,其餘文件加載該模塊,實際上就是讀取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 裏有調用到上面的 x.js:
// y.js define(function(require, exports, module) { var x = require('./x'); // 沒法馬上獲得模塊 x 的屬性 a console.log(x.a); // undefined });
這就是 CMD 模塊定義規範的全部內容。下一篇咱們開始介紹CMD規範的產物SeaJs加載器,進一步瞭解CMD規範的具體實現。
參考連接