模塊化開發(fā)

前端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)有SeaJSRequireJS在國外用的比較多,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)行

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容