前端JS模塊化開發(fā)有兩大規(guī)范AMD合CMD,下面以我的理解來簡單總結(jié)一下模塊化開發(fā)的思想 ,以及js前端常用的AMD和CMD 的區(qū)別,AMD代表是requireJs(異步加載),CMD代表seaJs。
一,模塊化的誕生
? ? 當(dāng)一個(gè)項(xiàng)目開發(fā)的越來越復(fù)雜的時(shí)候,就會(huì)出現(xiàn)一些命名沖突,或者文件依賴(后一個(gè)js必須依賴前一個(gè)js文件)等問題,所以需要進(jìn)行模塊化開發(fā),來提升開發(fā)效率,以及方面后期維護(hù)。
二,模塊化開發(fā)的演變
1.全局函數(shù)
? ? ? ? 寫法:

缺點(diǎn):1.污染全局變量,不能阻止變量命名沖突? 2.模塊之間看不出聯(lián)系
2.對象封裝
寫法

缺點(diǎn):1,暴露了所有成員,內(nèi)部對象可以被篡改,不安全 2.一定程度上解決了變量沖突,但是命名空間會(huì)越來越長
3,私有公有成員分離
寫法

優(yōu)點(diǎn): 1.利用閉包將函數(shù)包裝成一個(gè)獨(dú)立的作用域,私有空間的變量和函數(shù)不會(huì)影響全局作用域? 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ù)了,因?yàn)樵贓S6中已經(jīng)有了模塊化的實(shí)現(xiàn),隨著ES6的普及,第三方的模塊化實(shí)現(xiàn)將會(huì)慢慢的淘汰(但是這個(gè)在國內(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ī)范中,一個(gè)模塊就是一個(gè)js文件
define是一個(gè)全局函數(shù),用來定義模塊
define(factory)
對象{}這種方式,外部會(huì)直接獲取到該對象
字符串''同上
函數(shù)function( require, exports, module ){ // 模塊代碼 }
注意:為了減少出錯(cuò),定義函數(shù)的時(shí)候直接把這三個(gè)參數(shù)寫上
factory為對象、字符串時(shí),表示模塊的接口就是該對象、字符串。比如可以如下定義一個(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、exports和 module:
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){// 模塊代碼});
id和 deps參數(shù)可以省略。省略時(shí),可以通過構(gòu)建工具自動(dòng)生成。
注意:帶 id和 deps參數(shù)的 define用法不屬于 CMD 規(guī)范,而屬于Modules/Transport規(guī)范。
define.cmdObject
一個(gè)空對象,可用來判定當(dāng)前頁面是否有 CMD 模塊加載器:
if(typeofdefine ==="function"&& define.cmd) {// 有 Sea.js 等 CMD 模塊加載器存在}
requireFunction
require是 factory函數(shù)的第一個(gè)參數(shù)。
requirerequire(id)
require是一個(gè)方法,接受模塊標(biāo)識(shí)作為唯一參數(shù),用來獲取其他模塊提供的接口。
define(function(require, exports){// 獲取模塊 a 的接口 vara =require('./a');// 調(diào)用模塊 a 的方法 a.doSomething();});
注意:在開發(fā)時(shí),require的書寫需要遵循一些簡單約定。
require.async
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.resolverequire.resolve(id)
使用模塊系統(tǒng)內(nèi)部的路徑解析機(jī)制來解析并返回模塊路徑。該函數(shù)不會(huì)加載模塊,只返回解析后的絕對路徑。
define(function(require, exports){console.log(require.resolve('./b'));// ==> http://example.com/path/to/b.js});
這可以用來獲取模塊路徑,一般用在插件環(huán)境或需動(dòng)態(tài)拼接模塊路徑的場景下。
4. exports 和 module.exports
功能:通過給 exports或module.exports動(dòng)態(tài)的掛載變量、函數(shù)或?qū)ο?外部會(huì)獲取到該接口
exports 等價(jià)于 module.exports
可以通過多次給exports 掛載屬性向外暴露
不能直接給 exports 賦值
如果想暴露單個(gè)變量、函數(shù)或?qū)ο罂梢酝ㄟ^直接給module.exports 賦值 即可
5. exports
exports是一個(gè)對象,用來向外提供模塊接口。
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 模塊。
特別注意:下面這種寫法是錯(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 賦值是無效的,不能用來更改模塊接口。
6. moduleObject
module是一個(gè)對象,上面存儲(chǔ)了與當(dāng)前模塊相關(guān)聯(lián)的一些屬性和方法。
6.1 module.idString
模塊的唯一標(biāo)識(shí)。
define('id', [],function(require, exports, module){// 模塊代碼});
上面代碼中,define的第一個(gè)參數(shù)就是模塊標(biāo)識(shí)。
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ù)時(shí)),module.id 的值就是 module.uri,兩者完全相同。
6.3 module.dependenciesArray
dependencies是一個(gè)數(shù)組,表示當(dāng)前模塊的依賴。
6.4 module. exportsObject
當(dāng)前模塊對外提供的接口。
傳給 factory 構(gòu)造方法的 exports 參數(shù)是 module.exports 對象的一個(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=newSomeClass();// exports 不再等于 module.exportsconsole.log(module.exports===exports);// false});
注意:對 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){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 這五個(gè)。其他 API 有個(gè)印象就好,在需要時(shí)再來查文檔,不用刻意去記。
與 RequireJS 的 AMD 規(guī)范相比,CMD 規(guī)范盡量保持簡單,并與 CommonJS 和 Node.js 的 Modules 規(guī)范保持了很大的兼容性。通過 CMD 規(guī)范書寫的模塊,可以很容易在 Node.js 中運(yùn)行