AMD勋又、CMD枉层、RequireJS

為什么要使用模塊化?

  • 最主要的目的:

    • 解決命名沖突
    • 依賴管理
  • 其他價值

    • 提高代碼可讀性
    • 代碼解耦赐写,提高復用性

CMD、AMD膜赃、CommonJS 規(guī)范分別指什么挺邀?有哪些應用

這三個規(guī)范都是為javascript模塊化加載而生的,都是在用到或者預計要用到某些模塊時候加載該模塊跳座,使得大量的系統(tǒng)巨大的龐雜的代碼得以很好的組織和管理端铛。模塊化使得我們在使用和管理代碼的時候不那么混亂,而且也方便了多人的合作疲眷。

CMD規(guī)范

  • CMD(Common Module Definition)通用模板定義禾蚕,它是在一個瀏覽器端模塊化的開發(fā)規(guī)范
  • 使用CMD規(guī)范進行開發(fā)需要使用SeaJS
  • Sea.js推薦一個模塊為一個文件

代碼的書寫格式:

define(id?狂丝,dependencies换淆?,factory);
  • id:(可不寫几颜,默認文件名)倍试,用來定義模塊的標識,通用文件名作為模塊ID
  • dependencies:(可不寫)一個當前模塊依賴的模塊名稱數(shù)組(因為CMD推崇依賴就近蛋哭,因此一般不在此處指定)
  • factory:
    • function(require,exports,module)
    • require(id):require是一個方法县习,接受模塊標識作為唯一參數(shù),用來獲取其他模塊提供的接口
    • exports是一個對象谆趾,用來向外提供模塊接口
    • module是一個對象躁愿,上面儲存了與當前模塊相關聯(lián)的一些屬性和方法

define Function


define是一個全局函數(shù),用來定義模塊
define接受factory參數(shù)沪蓬,factory可以是一個函數(shù)彤钟,也可以是一個對象或字符串
factory為對象、字符串時怜跑,表示模塊的接口就是該對象样勃、字符串吠勘。例如定義一個JSON數(shù)據(jù)模塊:

define({ “foo”:“bar” });

factory為函數(shù)時,表示模塊的構造方法峡眶。執(zhí)行該構造方法剧防,可以得到模塊向外提供的接口。factory方法在執(zhí)行時辫樱,默認傳入三個參數(shù):require峭拘、exports、module:

define(function(require,exports,module){
})

define define(id?, deps?, factory)

define 也可以接受兩個以上參數(shù)狮暑。字符串 id 表示模塊標識鸡挠,數(shù)組 deps 是模塊依賴。比如:

define('hello', ['jquery'], function(require, exports, module) {
});

id和 deps參數(shù)可以省略搬男。省略時拣展,可以通過構建工具自動生成。
注意:帶 id和 deps參數(shù)的 define用法不屬于 CMD 規(guī)范缔逛,而屬于 Modules/Transport 規(guī)范备埃。

define.cmd Object

一個空對象,可用來判定當前頁面是否有 CMD 模塊加載器:

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

require Function


require是factory函數(shù)的第一個參數(shù)
require是一個方法褐奴,接受 模塊標識 作為唯一參數(shù)按脚,用來獲取其他模塊提供的接口。

define(function(require, exports) {

  // 獲取模塊 a 的接口
  var a = require('./a');

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

});

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

require.async 方法用來在模塊內部異步加載模塊敦冬,并在加載完成后執(zhí)行指定回調辅搬。callback 參數(shù)可選。

define(function(require, exports, module) {

  // 異步加載一個模塊脖旱,在加載完成時堪遂,執(zhí)行回調
  require.async('./b', function(b) {
    b.doSomething();
  });

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

});

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

require.resolve require.resolve(id)

使用模塊系統(tǒng)內部的路徑解析機制來解析并返回模塊路徑竿滨。該函數(shù)不會加載模塊,只返回解析后的絕對路徑捏境。

define(function(require, exports) {

  console.log(require.resolve('./b'));
  // ==> http://example.com/path/to/b.js

});

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

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) {

  // 錯誤用法5S恰芹缔!!
  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 是一個對象芝硬,上面存儲了與當前模塊相關聯(lián)的一些屬性和方法。

module.id String
模塊的唯一標識

define('id', [], function(require, exports, module) {

  // 模塊代碼

});

上面代碼中轧房,define 的第一個參數(shù)就是模塊標識拌阴。

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

define(function(require, exports, module) {

  console.log(module.uri); 
  // ==> http://example.com/path/to/this/file.js

});

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

module.dependencies Array
dependencies 是一個數(shù)組实辑,表示當前模塊的依賴。

module.exports Object
當前模塊對外提供的接口藻丢。
傳給 factory 構造方法的 exports 參數(shù)是 module.exports 對象的一個引用剪撬。只通過 exports 參數(shù)來提供接口,有時無法滿足開發(fā)者的所有需求悠反。 比如當模塊的接口是某個類的實例時残黑,需要通過 module.exports 來實現(xiàn):

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 的賦值需要同步執(zhí)行,不能放在回調函數(shù)里斋否。下面這樣是不行的:

// 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

});

小結
經常使用的 API 只有 define, require, require.async, exports, module.exports 這五個梨水。其他 API 有個印象就好。
與 RequireJS 的 AMD 規(guī)范相比茵臭,CMD 規(guī)范盡量保持簡單疫诽,并與 CommonJS 和 Node.js 的 Modules 規(guī)范保持了很大的兼容性。通過 CMD 規(guī)范書寫的模塊旦委,可以很容易在 Node.js 中運行奇徒。

AMD規(guī)范

  • AMD (Asynchronous Module Definition, 異步模塊定義) 指定一種機制,在該機制下模塊和依賴可以異步加載缨硝。這對瀏覽器端的異步加載尤其適用摩钙。
  • AMD 是 RequireJS 在推廣過程中對模塊定義的規(guī)范化產出。
  • 使用AMD規(guī)范進行開發(fā)需要使用RequireJS
  • requireJS主要解決兩個問題:
    • js文件之間的依賴關系:被依賴的文件需要早于依賴它的文件加載到瀏覽器
    • js加載的時候瀏覽器會停止頁面渲染查辩,加載文件越多胖笛,頁面失去響應時間越長
define(id?, dependencies?, factory);
  • id: 定義中模塊的名字网持,可選;如果沒有提供該參數(shù)长踊,模塊的名字應該默認為模塊加載器請求的指定腳本的名字功舀。

  • 依賴dependencies:是一個當前模塊依賴的,已被模塊定義的模塊標識的數(shù)組字面量之斯。 依賴參數(shù)是可選的日杈,如果忽略此參數(shù),它應該默認為["require", "exports", "module"]佑刷。然而莉擒,如果工廠方法的長度屬性小于3,加載器會選擇以函數(shù)的長度屬性指定的參數(shù)個數(shù)調用工廠方法瘫絮。

  • 工廠方法factory涨冀,模塊初始化要執(zhí)行的函數(shù)或對象。如果為函數(shù)麦萤,它應該只被執(zhí)行一次鹿鳖。如果是對象,此對象應該為模塊的輸出值壮莹。

AMD模式
define和require這兩個定義模塊翅帜、調用模塊的方法,合稱為AMD模式命满。它的模塊定義的方法非常清晰涝滴,不會污染全局環(huán)境,能夠清楚地顯示依賴關系胶台。
AMD模式可以用于瀏覽器環(huán)境歼疮,并且允許非同步加載模塊,也可以根據(jù)需要動態(tài)加載模塊诈唬。

define方法:定義模塊

define方法用于定義模塊韩脏,RequireJS要求每個模塊放在一個單獨的文件里。
按照是否依賴其他模塊铸磅,可以分成兩種情況討論赡矢。第一種情況是定義獨立模塊,即所定義的模塊不依賴其他模塊阅仔;第二種情況是定義非獨立模塊济竹,即所定義的模塊依賴于其他模塊。

require方法:調用模塊

require方法用于調用模塊霎槐。它的參數(shù)與define方法類似送浊。

require(['foo', 'bar'], function ( foo, bar ) {
        foo.doSomething();
});

上面方法表示加載foo和bar兩個模塊,當這兩個模塊都加載成功后丘跌,執(zhí)行一個回調函數(shù)袭景。該回調函數(shù)就用來完成具體的任務唁桩。
require方法的第一個參數(shù),是一個表示依賴關系的數(shù)組耸棒。這個數(shù)組可以寫得很靈活荒澡,請看下面的例子。

require( [ window.JSON ? undefined : 'util/json2' ], function ( JSON ) {
  JSON = JSON || window.JSON;
  console.log( JSON.parse( '{ "JSON" : "HERE" }' ) );
});

上面代碼加載JSON模塊時与殃,首先判斷瀏覽器是否原生支持JSON對象单山。如果是的,則將undefined傳入回調函數(shù)幅疼,否則加載util目錄下的json2模塊米奸。
require方法也可以用在define方法內部。

define(function (require) {
   var otherModule = require('otherModule');
});

CommonJS規(guī)范

CommonJS是服務器端模塊的規(guī)范爽篷,Node.js采用了這個規(guī)范悴晰。Node.JS首先采用了js模塊化的概念。
1.在一個模塊中逐工,存在一個自由的變量“require”铡溪,它是一個函數(shù)。

  • 這個“require”函數(shù)接收一個模塊標識符
  • “require”返回外部模塊所輸出的API
  • 如果出現(xiàn)依賴閉環(huán)(dependency cycle)泪喊,那么外部模塊在被它的傳遞依賴(transitive dependencies)所require的時候可能并沒有執(zhí)行完成棕硫;在這種情況下,”require”返回的對象必須至少包含此外部模塊在調用require函數(shù)(會進入當前模塊執(zhí)行環(huán)境)之前就已經準備完畢的輸出袒啼。
  • 如果請求的模塊不能返回哈扮,那么”require”必須拋出一個錯誤。

2.在一個模塊中瘤泪,會存在一個名為“exports”的自由變量,它是一個對象育八,模塊可以在執(zhí)行的時候把自身的API加入到其中对途。

3.模塊必須使用“exports”對象來做為輸出的唯一表示。

根據(jù)這個規(guī)范髓棋,每個文件就是一個模塊实檀,有自己的作用域。在一個文件里面定義的變量按声、函數(shù)膳犹、類,都是私有的签则,對其他文件不可見须床。

// example.js
var x = 5;
var addX = function (value) {
  return value + x;
};

上面代碼中,變量X和函數(shù)addX渐裂,是當前文件example.js私有的豺旬,其他文件不可見钠惩。如果想在多個文件分享變量,必須定義為global對象的屬性族阅。

global.warning = true;

上面代碼的warning變量篓跛,可以被所有文件讀取。當然坦刀,這樣寫法是不推薦的愧沟。

CommonJS規(guī)范規(guī)定,每個模塊內部鲤遥,module變量代表當前模塊沐寺。這個變量是一個對象,它的exports屬性(即module.exports)是對外的接口渴频。加載某個模塊芽丹,其實是加載該模塊的module.exports屬性。

var x = 5;
var addX = function (value) {
  return value + x;
};
module.exports.x = x;
module.exports.addX = addX;

上面代碼通過module.exports輸出變量x和函數(shù)addX卜朗。

require方法用于加載模塊拔第。

var example = require('./example.js');

console.log(example.x); // 5
console.log(example.addX(1)); // 6

CommonJS模塊的特點:

  • 所有代碼都運行在模塊作用域,不會污染全局作用域场钉。
  • 模塊可以多次加載蚊俺,但是只會在第一次加載時運行一次,然后運行結果就被緩存了逛万,以后再加載泳猬,就直接讀取緩存結果。要想讓模塊再次運行宇植,必須清除緩存得封。
  • 模塊加載的順序,按照其在代碼中出現(xiàn)的順序指郁。

RequireJS模塊化加載實例

r.js打包應用
預覽
代碼

【個人總結忙上,如有錯漏,歡迎指出】
:>

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末闲坎,一起剝皮案震驚了整個濱河市疫粥,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌腰懂,老刑警劉巖梗逮,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異绣溜,居然都是意外死亡慷彤,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瞬欧,“玉大人贷屎,你說我怎么就攤上這事∷一ⅲ” “怎么了唉侄?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長野建。 經常有香客問我属划,道長,這世上最難降的妖魔是什么候生? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任同眯,我火速辦了婚禮,結果婚禮上唯鸭,老公的妹妹穿的比我還像新娘须蜗。我一直安慰自己,他們只是感情好目溉,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布明肮。 她就那樣靜靜地躺著,像睡著了一般缭付。 火紅的嫁衣襯著肌膚如雪柿估。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天陷猫,我揣著相機與錄音秫舌,去河邊找鬼。 笑死绣檬,一個胖子當著我的面吹牛足陨,可吹牛的內容都是我干的。 我是一名探鬼主播娇未,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼墨缘,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了忘蟹?” 一聲冷哼從身側響起飒房,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤搁凸,失蹤者是張志新(化名)和其女友劉穎媚值,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體护糖,經...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡褥芒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锰扶。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡献酗,死狀恐怖,靈堂內的尸體忽然破棺而出坷牛,到底是詐尸還是另有隱情罕偎,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布京闰,位于F島的核電站颜及,受9級特大地震影響,放射性物質發(fā)生泄漏蹂楣。R本人自食惡果不足惜俏站,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望痊土。 院中可真熱鬧肄扎,春花似錦、人聲如沸赁酝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赞哗。三九已至雷则,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間肪笋,已是汗流浹背月劈。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留藤乙,地道東北人猜揪。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像坛梁,于是被迫代替她去往敵國和親而姐。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內容

  • 題目1: 為什么要使用模塊化划咐? 最主要的目的:1.解決命名沖突2.依賴管理其他價值:1.提高代碼可讀性2.代碼解耦...
    saintkl閱讀 309評論 0 0
  • 為什么要使用模塊化拴念? 1.解決命名沖突2.依賴管理3.提高代碼可讀性4.代碼解耦,提高復用性 CMD褐缠、AMD政鼠、Co...
    小囧兔閱讀 247評論 0 0
  • 題目1:為什么要使用模塊化? 最主要的目的:解決命名沖突依賴管理其他價值提高代碼可讀性代碼解耦队魏,提高復用性 在Ja...
    無目的閱讀 356評論 0 0
  • 中華公般,最令世界矚目的不是長城,也不是黃河,而是綿延不絕官帘,一脈相承之中國文化瞬雹。 文化,一個多么飽滿的詞匯刽虹!它是高修...
    曹九萬閱讀 1,903評論 3 3
  • 畢業(yè)季酗捌,我去年就已經畢業(yè)了,按理來說這樣的一個畢業(yè)季應該與我無關才對涌哲,但人就是往往被一些無關的事攪得心情不快意敛。 一...
    道生一_79bb閱讀 163評論 2 0