CMD 模塊定義規(guī)范

2018-11-06 18:23 更新

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

在 CMD 規(guī)范中,一個(gè)模塊就是一個(gè)文件。代碼的書寫格式如下:

define(factory);

define Function

define 是一個(gè)全局函數(shù),用來定義模塊。

define define(factory)

define 接受 factory 參數(shù),factory 可以是一個(gè)函數(shù),也可以是一個(gè)對(duì)象或字符串。

factory 為對(duì)象、字符串時(shí),表示模塊的接口就是該對(duì)象、字符串。比如可以如下定義一個(gè) JSON 數(shù)據(jù)模塊:

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

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

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

factory 為函數(shù)時(shí),表示是模塊的構(gòu)造方法。執(zhí)行該構(gòu)造方法,可以得到模塊向外提供的接口。factory 方法在執(zhí)行時(shí),默認(rèn)會(huì)傳入三個(gè)參數(shù):require、exportsmodule

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

define define(id?, deps?, factory)

define 也可以接受兩個(gè)以上參數(shù)。字符串 id 表示模塊標(biāo)識(shí),數(shù)組 deps 是模塊依賴。比如:

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

iddeps 參數(shù)可以省略。省略時(shí),可以通過構(gòu)建工具自動(dòng)生成。

注意:帶 iddeps 參數(shù)的 define 用法不屬于 CMD 規(guī)范,而屬于 Modules/Transport 規(guī)范。

define.cmd Object

一個(gè)空對(duì)象,可用來判定當(dāng)前頁面是否有 CMD 模塊加載器:

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

require Function

requirefactory 函數(shù)的第一個(gè)參數(shù)。

require require(id)

require 是一個(gè)方法,接受 模塊標(biāo)識(shí) 作為唯一參數(shù),用來獲取其他模塊提供的接口。

define(function(require, exports) {  // 獲取模塊 a 的接口
  var a = require('./a');  // 調(diào)用模塊 a 的方法
  a.doSomething();

});

注意:在開發(fā)時(shí),require 的書寫需要遵循一些 簡單約定

require.async require.async(id, callback?)

require.async 方法用來在模塊內(nèi)部異步加載模塊,并在加載完成后執(zhí)行指定回調(diào)。callback 參數(shù)可選。

define(function(require, exports, module) {  // 異步加載一個(gè)模塊,在加載完成時(shí),執(zhí)行回調(diào)
  require.async('./b', function(b) {
    b.doSomething();
  });  // 異步加載多個(gè)模塊,在加載完成時(shí),執(zhí)行回調(diào)
  require.async(['./c', './d'], function(c, d) {
    c.doSomething();
    d.doSomething();
  });

});

注意require 是同步往下執(zhí)行,require.async 則是異步回調(diào)執(zhí)行。require.async 一般用來加載可延遲異步加載的模塊。

require.resolve require.resolve(id)

使用模塊系統(tǒng)內(nèi)部的路徑解析機(jī)制來解析并返回模塊路徑。該函數(shù)不會(huì)加載模塊,只返回解析后的絕對(duì)路徑。

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

這可以用來獲取模塊路徑,一般用在插件環(huán)境或需動(dòng)態(tài)拼接模塊路徑的場(chǎng)景下。

exports Object

exports 是一個(gè)對(duì)象,用來向外提供模塊接口。

define(function(require, exports) {  // 對(duì)外提供 foo 屬性
  exports.foo = 'bar';  // 對(duì)外提供 doSomething 方法
  exports.doSomething = function() {};

});

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

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

});

如果 return 語句是模塊中的唯一代碼,還可簡化為:

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

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

特別注意:下面這種寫法是錯(cuò)誤的!

define(function(require, exports) {  // 錯(cuò)誤用法??!!
  exports = {
    foo: 'bar',    doSomething: function() {}
  };

});

正確的寫法是用 return 或者給 module.exports 賦值:

define(function(require, exports, module) {  // 正確寫法
  module.exports = {
    foo: 'bar',    doSomething: function() {}
  };

});

提示exports 僅僅是 module.exports 的一個(gè)引用。在 factory 內(nèi)部給 exports 重新賦值時(shí),并不會(huì)改變 module.exports 的值。因此給 exports 賦值是無效的,不能用來更改模塊接口。

module Object

module 是一個(gè)對(duì)象,上面存儲(chǔ)了與當(dāng)前模塊相關(guān)聯(lián)的一些屬性和方法。

module.id String

模塊的唯一標(biāo)識(shí)。

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

上面代碼中,define 的第一個(gè)參數(shù)就是模塊標(biāo)識(shí)。

module.uri String

根據(jù)模塊系統(tǒng)的路徑解析規(guī)則得到的模塊絕對(duì)路徑。

define(function(require, exports, module) {  console.log(module.uri); 
  // ==> http://example.com/path/to/this/file.js});

一般情況下(沒有在 define 中手寫 id 參數(shù)時(shí)),module.id 的值就是 module.uri,兩者完全相同。

module.dependencies Array

dependencies 是一個(gè)數(shù)組,表示當(dāng)前模塊的依賴。

module.exports Object

當(dāng)前模塊對(duì)外提供的接口。

傳給 factory 構(gòu)造方法的 exports 參數(shù)是 module.exports 對(duì)象的一個(gè)引用。只通過 exports 參數(shù)來提供接口,有時(shí)無法滿足開發(fā)者的所有需求。 比如當(dāng)模塊的接口是某個(gè)類的實(shí)例時(shí),需要通過 module.exports 來實(shí)現(xiàn):

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

  // 重新給 module.exports 賦值
  module.exports = new SomeClass();  // exports 不再等于 module.exports
  console.log(module.exports === exports); // false});

注意:對(duì) module.exports 的賦值需要同步執(zhí)行,不能放在回調(diào)函數(shù)里。下面這樣是不行的:

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

});

在 y.js 里有調(diào)用到上面的 x.js:

// y.jsdefine(function(require, exports, module) {  var x = require('./x');  // 無法立刻得到模塊 x 的屬性 a
  console.log(x.a); // undefined});

小結(jié)

這就是 CMD 模塊定義規(guī)范的所有內(nèi)容。經(jīng)常使用的 API 只有 define, require, require.async, exports, module.exports 這五個(gè)。其他 API 有個(gè)印象就好,在需要時(shí)再來查文檔,不用刻意去記。

與 RequireJS 的 AMD 規(guī)范相比,CMD 規(guī)范盡量保持簡單,并與 CommonJS 和 Node.js 的 Modules 規(guī)范保持了很大的兼容性。通過 CMD 規(guī)范書寫的模塊,可以很容易在 Node.js 中運(yùn)行,后續(xù)會(huì)介紹。

祝使用愉快,有任何想法建議,歡迎反饋留言。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)