AMD、CMD剩岳、RequireJS

為什么要使用模塊化贞滨?

最主要的目的:

解決命名沖突

依賴管理

其他價值

提高代碼可讀性

代碼解耦,提高復(fù)用性

CMD卢肃、AMD疲迂、CommonJS 規(guī)范分別指什么?有哪些應(yīng)用

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

CMD規(guī)范

CMD(Common Module Definition)通用模板定義尾组,它是在一個瀏覽器端模塊化的開發(fā)規(guī)范

使用CMD規(guī)范進(jìn)行開發(fā)需要使用SeaJS

Sea.js推薦一個模塊為一個文件

代碼的書寫格式:

define(id忙芒?,dependencies讳侨?呵萨,factory);

id:(可不寫,默認(rèn)文件名)跨跨,用來定義模塊的標(biāo)識潮峦,通用文件名作為模塊ID

dependencies:(可不寫)一個當(dāng)前模塊依賴的模塊名稱數(shù)組(因?yàn)镃MD推崇依賴就近囱皿,因此一般不在此處指定)

factory:

function(require,exports,module)

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

exports是一個對象嘱腥,用來向外提供模塊接口

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

define Function

define是一個全局函數(shù)拘悦,用來定義模塊

define接受factory參數(shù)齿兔,factory可以是一個函數(shù),也可以是一個對象或字符串

factory為對象础米、字符串時分苇,表示模塊的接口就是該對象、字符串屁桑。例如定義一個JSON數(shù)據(jù)模塊:

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

factory為函數(shù)時组砚,表示模塊的構(gòu)造方法。執(zhí)行該構(gòu)造方法掏颊,可以得到模塊向外提供的接口糟红。factory方法在執(zhí)行時,默認(rèn)傳入三個參數(shù):require乌叶、exports盆偿、module:

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

define define(id?, deps?, factory)

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

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

id和 deps參數(shù)可以省略。省略時乐横,可以通過構(gòu)建工具自動生成求橄。

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

define.cmd Object

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

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

require Function

require是factory函數(shù)的第一個參數(shù)

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

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

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

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

define(function(require, exports, module){// 異步加載一個模塊,在加載完成時旋圆,執(zhí)行回調(diào)require.async('./b',function(b){? ? b.doSomething();? });// 異步加載多個模塊宠默,在加載完成時,執(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ù)不會加載模塊艾君,只返回解析后的絕對路徑采够。

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){// 錯誤用法!要门!!exports = {foo:'bar',doSomething:function(){}? };});

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

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

提示:exports 僅僅是 module.exports 的一個引用虏肾。在 factory 內(nèi)部給 exports 重新賦值時,并不會改變 module.exports 的值欢搜。因此給 exports 賦值是無效的封豪,不能用來更改模塊接口。

module Object

module 是一個對象炒瘟,上面存儲了與當(dāng)前模塊相關(guān)聯(lián)的一些屬性和方法吹埠。

module.id String

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

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

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

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ù)組,表示當(dāng)前模塊的依賴受啥。

module.exports Object

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

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

define(function(require,exports,module) {// exports 是 module.exports 的一個引用console.log(module.exports===exports);// true// 重新給 module.exports 賦值module.exports=newSomeClass();// exports 不再等于 module.exportsconsole.log(module.exports===exports);// false});

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

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

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

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

小結(jié)

經(jīng)常使用的 API 只有 define, require, require.async, exports, module.exports 這五個糯景。其他 API 有個印象就好嘁圈。

與 RequireJS 的 AMD 規(guī)范相比省骂,CMD 規(guī)范盡量保持簡單,并與 CommonJS 和 Node.js 的 Modules 規(guī)范保持了很大的兼容性最住。通過 CMD 規(guī)范書寫的模塊钞澳,可以很容易在 Node.js 中運(yùn)行。

AMD規(guī)范

AMD (Asynchronous Module Definition, 異步模塊定義) 指定一種機(jī)制涨缚,在該機(jī)制下模塊和依賴可以異步加載轧粟。這對瀏覽器端的異步加載尤其適用。

AMD 是 RequireJS 在推廣過程中對模塊定義的規(guī)范化產(chǎn)出脓魏。

使用AMD規(guī)范進(jìn)行開發(fā)需要使用RequireJS

requireJS主要解決兩個問題:

js文件之間的依賴關(guān)系:被依賴的文件需要早于依賴它的文件加載到瀏覽器

js加載的時候?yàn)g覽器會停止頁面渲染兰吟,加載文件越多,頁面失去響應(yīng)時間越長

define(id?, dependencies?, factory);

id: 定義中模塊的名字茂翔,可選混蔼;如果沒有提供該參數(shù),模塊的名字應(yīng)該默認(rèn)為模塊加載器請求的指定腳本的名字珊燎。

依賴dependencies:是一個當(dāng)前模塊依賴的惭嚣,已被模塊定義的模塊標(biāo)識的數(shù)組字面量。 依賴參數(shù)是可選的悔政,如果忽略此參數(shù)晚吞,它應(yīng)該默認(rèn)為["require", "exports", "module"]。然而卓箫,如果工廠方法的長度屬性小于3载矿,加載器會選擇以函數(shù)的長度屬性指定的參數(shù)個數(shù)調(diào)用工廠方法。

工廠方法factory烹卒,模塊初始化要執(zhí)行的函數(shù)或?qū)ο竺瓶H绻麨楹瘮?shù),它應(yīng)該只被執(zhí)行一次旅急。如果是對象逢勾,此對象應(yīng)該為模塊的輸出值。

AMD模式

define和require這兩個定義模塊藐吮、調(diào)用模塊的方法溺拱,合稱為AMD模式。它的模塊定義的方法非常清晰谣辞,不會污染全局環(huán)境迫摔,能夠清楚地顯示依賴關(guān)系。

AMD模式可以用于瀏覽器環(huán)境泥从,并且允許非同步加載模塊句占,也可以根據(jù)需要動態(tài)加載模塊。

define方法:定義模塊

define方法用于定義模塊躯嫉,RequireJS要求每個模塊放在一個單獨(dú)的文件里纱烘。

按照是否依賴其他模塊杨拐,可以分成兩種情況討論。第一種情況是定義獨(dú)立模塊擂啥,即所定義的模塊不依賴其他模塊哄陶;第二種情況是定義非獨(dú)立模塊,即所定義的模塊依賴于其他模塊哺壶。

require方法:調(diào)用模塊

require方法用于調(diào)用模塊屋吨。它的參數(shù)與define方法類似。

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

上面方法表示加載foo和bar兩個模塊变骡,當(dāng)這兩個模塊都加載成功后离赫,執(zhí)行一個回調(diào)函數(shù)。該回調(diào)函數(shù)就用來完成具體的任務(wù)塌碌。

require方法的第一個參數(shù)渊胸,是一個表示依賴關(guān)系的數(shù)組。這個數(shù)組可以寫得很靈活,請看下面的例子。

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

上面代碼加載JSON模塊時囱持,首先判斷瀏覽器是否原生支持JSON對象妖碉。如果是的谤逼,則將undefined傳入回調(diào)函數(shù),否則加載util目錄下的json2模塊。

require方法也可以用在define方法內(nèi)部。

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

CommonJS規(guī)范

CommonJS是服務(wù)器端模塊的規(guī)范疫稿,Node.js采用了這個規(guī)范。Node.JS首先采用了js模塊化的概念鹃两。

1.在一個模塊中遗座,存在一個自由的變量“require”,它是一個函數(shù)俊扳。

這個“require”函數(shù)接收一個模塊標(biāo)識符

“require”返回外部模塊所輸出的API

如果出現(xiàn)依賴閉環(huán)(dependency cycle)途蒋,那么外部模塊在被它的傳遞依賴(transitive dependencies)所require的時候可能并沒有執(zhí)行完成;在這種情況下馋记,”require”返回的對象必須至少包含此外部模塊在調(diào)用require函數(shù)(會進(jìn)入當(dāng)前模塊執(zhí)行環(huán)境)之前就已經(jīng)準(zhǔn)備完畢的輸出号坡。

如果請求的模塊不能返回,那么”require”必須拋出一個錯誤梯醒。

2.在一個模塊中宽堆,會存在一個名為“exports”的自由變量,它是一個對象茸习,模塊可以在執(zhí)行的時候把自身的API加入到其中日麸。

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

根據(jù)這個規(guī)范逮光,每個文件就是一個模塊代箭,有自己的作用域。在一個文件里面定義的變量涕刚、函數(shù)嗡综、類,都是私有的杜漠,對其他文件不可見极景。

// example.jsvarx =5;varaddX =function(value){returnvalue + x;};

上面代碼中,變量X和函數(shù)addX驾茴,是當(dāng)前文件example.js私有的盼樟,其他文件不可見。如果想在多個文件分享變量锈至,必須定義為global對象的屬性晨缴。

global.warning =true;

上面代碼的warning變量,可以被所有文件讀取峡捡。當(dāng)然击碗,這樣寫法是不推薦的。

CommonJS規(guī)范規(guī)定们拙,每個模塊內(nèi)部稍途,module變量代表當(dāng)前模塊。這個變量是一個對象砚婆,它的exports屬性(即module.exports)是對外的接口械拍。加載某個模塊,其實(shí)是加載該模塊的module.exports屬性装盯。

varx =5;varaddX =function(value){returnvalue + x;};module.exports.x = x;module.exports.addX = addX;

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

require方法用于加載模塊。

varexample =require('./example.js');console.log(example.x);// 5console.log(example.addX(1));// 6

CommonJS模塊的特點(diǎn):

所有代碼都運(yùn)行在模塊作用域验夯,不會污染全局作用域猖吴。

模塊可以多次加載,但是只會在第一次加載時運(yùn)行一次挥转,然后運(yùn)行結(jié)果就被緩存了海蔽,以后再加載,就直接讀取緩存結(jié)果绑谣。要想讓模塊再次運(yùn)行党窜,必須清除緩存。

模塊加載的順序借宵,按照其在代碼中出現(xiàn)的順序幌衣。

作者:Eazer

鏈接:http://www.reibang.com/p/c9177fbbe9aa

來源:簡書

著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處豁护。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末哼凯,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子楚里,更是在濱河造成了極大的恐慌断部,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件班缎,死亡現(xiàn)場離奇詭異蝴光,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)达址,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門蔑祟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人沉唠,你說我怎么就攤上這事疆虚。” “怎么了右冻?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵装蓬,是天一觀的道長。 經(jīng)常有香客問我纱扭,道長牍帚,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任乳蛾,我火速辦了婚禮暗赶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘肃叶。我一直安慰自己蹂随,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布因惭。 她就那樣靜靜地躺著岳锁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蹦魔。 梳的紋絲不亂的頭發(fā)上激率,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機(jī)與錄音勿决,去河邊找鬼乒躺。 笑死,一個胖子當(dāng)著我的面吹牛低缩,可吹牛的內(nèi)容都是我干的嘉冒。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼讳推!你這毒婦竟也來了顶籽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤娜遵,失蹤者是張志新(化名)和其女友劉穎蜕衡,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體设拟,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年久脯,在試婚紗的時候發(fā)現(xiàn)自己被綠了纳胧。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡帘撰,死狀恐怖跑慕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情摧找,我是刑警寧澤核行,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站蹬耘,受9級特大地震影響芝雪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜综苔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一惩系、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧如筛,春花似錦堡牡、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至妖胀,卻和暖如春芥颈,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背做粤。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工浇借, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人怕品。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓妇垢,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子闯估,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 為什么要使用模塊化? 最主要的目的:解決命名沖突依賴管理 其他價值提高代碼可讀性代碼解耦刚夺,提高復(fù)用性 CMD献丑、AM...
    Eazer閱讀 659評論 3 1
  • 題目1: 為什么要使用模塊化? 網(wǎng)頁愈趨向Web應(yīng)用程序侠姑,導(dǎo)致JavaScript的體量愈來愈大创橄,客戶端的代碼模塊...
    cctosuper閱讀 285評論 0 0
  • 題目1: 為什么要使用模塊化? 最主要的目的: 解決命名沖突 依賴管理 其他價值 提高代碼可讀性 代碼解耦莽红,提高復(fù)...
    曾祥輝閱讀 143評論 0 0
  • 文/長安有間鋪?zhàn)?海底月是天上月,眼前人是心上人鬼店。 我想每個女孩子都曾期待過能遇到一個人网棍,幻想著與他的幸福生活會是...
    長安有間鋪?zhàn)?/span>閱讀 585評論 1 3
  • 1、void remove(List a薪韩,Integer b) 刪除鏈表a中所有大于b的表項(xiàng)确沸。 2、創(chuàng)建線程的3...
    黃云豐閱讀 277評論 0 0