前端JS模塊化開發(fā)有兩大規(guī)范AMD合CMD寻定,下面以我的理解來簡單總結(jié)一下模塊化開發(fā)的思想 ,以及js前端常用的AMD和CMD 的區(qū)別,AMD代表是requireJs(異步加載),CMD代表seaJs酥夭。
一,模塊化的誕生
? ? 當(dāng)一個項目開發(fā)的越來越復(fù)雜的時候,就會出現(xiàn)一些命名沖突糊肤,或者文件依賴(后一個js必須依賴前一個js文件)等問題,所以需要進(jìn)行模塊化開發(fā)苔悦,來提升開發(fā)效率轩褐,以及方面后期維護(hù)。
二玖详,模塊化開發(fā)的演變
1.全局函數(shù)
? ? ? ? 寫法:
缺點(diǎn):1.污染全局變量,不能阻止變量命名沖突? 2.模塊之間看不出聯(lián)系
2.對象封裝
寫法
缺點(diǎn):1,暴露了所有成員勤讽,內(nèi)部對象可以被篡改蟋座,不安全 2.一定程度上解決了變量沖突,但是命名空間會越來越長
3脚牍,私有公有成員分離
寫法
優(yōu)點(diǎn): 1.利用閉包將函數(shù)包裝成一個獨(dú)立的作用域向臀,私有空間的變量和函數(shù)不會影響全局作用域? 2,以返回值的方式可以有選擇的對外暴漏公用方法诸狭,隱藏私有方法和變量? ? ?3券膀,一定意義上解決了命名沖突
4,模塊的擴(kuò)展與維護(hù)
優(yōu)點(diǎn): 1.有利于對龐大模塊的子模塊擴(kuò)展? 2.實(shí)現(xiàn)開閉原則:對新增模塊開放驯遇,對修改關(guān)閉
5.第三方依賴介入
原則:高內(nèi)聚低耦合芹彬,模塊內(nèi)相關(guān)性高,模塊間關(guān)聯(lián)低叉庐。
三舒帮,模塊化規(guī)范
服務(wù)器端規(guī)范主要是CommonJS,node.js用的就是CommonJS規(guī)范陡叠。
客戶端規(guī)范主要有:AMD(異步模塊定義玩郊,推崇依賴前置)、CMD(通用模塊定義枉阵,推崇依賴就近)译红。AMD規(guī)范的實(shí)現(xiàn)主要有RequireJS,CMD規(guī)范的主要實(shí)現(xiàn)有SeaJS兴溜。RequireJS在國外用的比較多侦厚,SeaJS在國內(nèi)用的比較多反璃,并且SeaJS的創(chuàng)始人為阿里的玉伯,所以SeaJS在阿里系用的非常廣泛假夺,包括京東等大廠也在用SeaJS淮蜈,我們詳細(xì)介紹的也是SeaJS。但是SeaJS已經(jīng)停止維護(hù)了已卷,因為在ES6中已經(jīng)有了模塊化的實(shí)現(xiàn)梧田,隨著ES6的普及,第三方的模塊化實(shí)現(xiàn)將會慢慢的淘汰(但是這個在國內(nèi)可能還要很多年)侧蘸。
四裁眯,seaJs
1.用法
(1)引入seajs
(2)定義模塊:define(function(require, exports, module){ 模塊代碼 });? ??require(‘模塊id’)? ?輸出內(nèi)容: exports.add = add;? 用法: sea.usa("id",function(模塊對象){業(yè)務(wù)代碼});
(3)實(shí)際應(yīng)用
在a.js內(nèi)寫cmd模塊化規(guī)范代碼如下
在index.html的應(yīng)用
2. 定義模塊define
先有規(guī)范,后有實(shí)現(xiàn)
在CMD規(guī)范中讳癌,一個模塊就是一個js文件
define是一個全局函數(shù)穿稳,用來定義模塊
define(factory)
對象{}這種方式,外部會直接獲取到該對象
字符串''同上
函數(shù)function( require, exports, module ){ // 模塊代碼 }
注意:為了減少出錯晌坤,定義函數(shù)的時候直接把這三個參數(shù)寫上
factory為對象逢艘、字符串時,表示模塊的接口就是該對象骤菠、字符串它改。比如可以如下定義一個 JSON 數(shù)據(jù)模塊:
define({"foo":"bar"});
也可以通過字符串定義模板模塊:
define('I am a template. My name is {{name}}.');
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.cmdObject
一個空對象嗅剖,可用來判定當(dāng)前頁面是否有 CMD 模塊加載器:
if(typeofdefine ==="function"&& define.cmd) {// 有 Sea.js 等 CMD 模塊加載器存在}
requireFunction
require是 factory函數(shù)的第一個參數(shù)。
requirerequire(id)
require是一個方法嘁扼,接受模塊標(biāo)識作為唯一參數(shù)信粮,用來獲取其他模塊提供的接口。
define(function(require, exports){// 獲取模塊 a 的接口 vara =require('./a');// 調(diào)用模塊 a 的方法 a.doSomething();});
注意:在開發(fā)時趁啸,require的書寫需要遵循一些簡單約定强缘。
require.async
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.resolverequire.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)拼接模塊路徑的場景下眯亦。
4. exports 和 module.exports
功能:通過給 exports或module.exports動態(tài)的掛載變量伤溉、函數(shù)或?qū)ο?外部會獲取到該接口
exports 等價于 module.exports
可以通過多次給exports 掛載屬性向外暴露
不能直接給 exports 賦值
如果想暴露單個變量、函數(shù)或?qū)ο罂梢酝ㄟ^直接給module.exports 賦值 即可
5. exports
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 賦值是無效的琢蛤,不能用來更改模塊接口。
6. moduleObject
module是一個對象抛虏,上面存儲了與當(dāng)前模塊相關(guān)聯(lián)的一些屬性和方法博其。
6.1 module.idString
模塊的唯一標(biāo)識。
define('id', [],function(require, exports, module){// 模塊代碼});
上面代碼中迂猴,define的第一個參數(shù)就是模塊標(biāo)識慕淡。
6.2 module.uriString
根據(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峰髓,兩者完全相同傻寂。
6.3 module.dependenciesArray
dependencies是一個數(shù)組,表示當(dāng)前模塊的依賴携兵。
6.4 module. exportsObject
當(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});
7. 小結(jié)
這就是 CMD 模塊定義規(guī)范的所有內(nèi)容。經(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)行