關(guān)于前端模塊化

1. 為什么要使用模塊化?

JS的發(fā)展初期 ,僅需實現(xiàn)簡單的用戶交互邏輯,而隨著CPU、瀏覽器性能的大幅度提升,很多頁面邏輯遷移到了客戶端,且隨著web2.0時代的到來,Ajax技術(shù)得到了廣泛的應(yīng)用,jQuery等前端庫層出不窮,前端代碼日益膨脹。此時JS極其簡單的代碼組織已無法駕馭如此龐大規(guī)模的代碼,此時參考其他的語言,例如JAVA有一個package的概念,將邏輯上相關(guān)的代碼組織到一個包里,每個包互相獨立,不會相互影響,這樣就可以將代碼分塊組織。而JS在設(shè)計之初,并未提供類似的功能,因此開發(fā)者開始模擬類似的功能,來隔離、組織復(fù)雜的JS代碼,這就是前端模塊化的開始。
模塊化最初的思路就是在一個文件中編寫幾個相關(guān)的函數(shù),需要的時候加載函數(shù)所在的文件,調(diào)用函數(shù)。然而這樣做污染了全局變量,無法保證不與其他模塊變量名沖突,且模塊成員之間沒有什么關(guān)系。
之后為解決上面的問題,采用對象的寫法,將所有的模塊成員封裝到一個對象中,var module = {fn1: function(){...}, fn2: function(){...}}在調(diào)用模塊的時候引用對應(yīng)的文件module.fn(),這樣做避免了污染變量,同時模塊里的成員也有了關(guān)系,但是在外部可以隨意修改內(nèi)部成員var module.fn1 = 1。
最后出現(xiàn)了一種思路,就是通過立即執(zhí)行函數(shù),來隱藏內(nèi)部的變量與函數(shù),立即執(zhí)行函數(shù)將內(nèi)部的變量與函數(shù)都包裹在自己的作用域中,外部無法修改,這種做法就是現(xiàn)階段模塊化的基礎(chǔ)。
目前JS模塊化規(guī)范主要有兩種:CommonJS和AMD。

2.CMD、AMD、CommonJS 規(guī)范分別指什么?有哪些應(yīng)用

CommonJS

CommonJS是在服務(wù)器端的規(guī)范,由Node.js發(fā)揚光大。CommonJS包含以下三部分:

  • 定義模塊:根據(jù)CommonJS規(guī)范,一個單獨的文件就是一個模塊。每一個模塊都是一個單獨的作用域,也即在該模塊內(nèi)部定義的變量,無法被其他模塊讀取,除非定義為global對象的屬性。
  • 模塊輸出:模塊只有一個出口,modules.export對象,我們把模塊希望輸出的內(nèi)容放入該對象。
  • 加載模塊:加載模塊使用require方法,該方法讀取一個文件并執(zhí)行,返回文件內(nèi)部的module.exports對象。
//定義一個模塊math.js
module.exports = function(){
  var sum = 0, i = 0, args = arguments
  while(i < args.length){
    sum += args[i++]
  }
  return sum;
};
//main.js加載模塊
var add = require('math.js')
console.log(add(1,2,3)) // 用node運行,輸出結(jié)果為6

上述代碼首先定義一個了模塊math.js,并通過module.exports輸出了一個函數(shù),接著在另一個文件中用require加載了這個函數(shù)。這就是CommonJS的基本用法。然而可以注意到,上面的代碼中require是同步的,模塊系統(tǒng)需同步讀取模塊文件內(nèi)容,并編譯執(zhí)行以得到模塊接口。這在服務(wù)器端可以實現(xiàn),但是在瀏覽器端,加載JS代碼最常見的方式是在document中插入script標簽,但script標簽天生是異步的,所以CommonJS規(guī)范無法在瀏覽器端實施。
所以就有一種思路,可以用一套標準模板來封裝模板定義,于是有了以下兩種規(guī)范:AMD和CMD。

AMD

AMD(Asynchronous Module Definition),異步模塊定義,是一個在瀏覽器端開發(fā)的模塊化規(guī)范。由于原生JS不支持AMD規(guī)范,因此要用到一個庫函數(shù)"require.js"。AMD采用異步加載的模式,模塊的加載不影響它后面語句的運行。所有依賴這個模塊的語句,都定義在一個回調(diào)函數(shù)中,等加載完成后,這個回調(diào)函數(shù)才會運行。依舊用上述的例子,首先在html里通過script標簽里引入require.js:
<script src="./require.js" data-main="./main.js"></script>
以上代碼中data-main屬性的作用是指定網(wǎng)頁程序的主模塊,即main.js會第一個被require.js加載。

//定義模塊math.js
define(function (){
    var add=function(){
        var sum=0,i=0,args = arguments;
        while(i < args.length){
            sum += args[i++];
        }
        return sum
    }
    return {
        add: add
    };
})
//main.js加載模塊
require(['math'],function(math){
    console.log(math.add(1,2,3))
}) // 在瀏覽器端運行,輸出結(jié)果為6。

requireJS定義了一個函數(shù)define,它是一個全局變量,用于定義模塊。語法為:
define([[id,] dependencies,] factory)

  • id 可選參數(shù),用于定義模塊的標識,如果沒有就默認為腳本文件名(去掉擴展名)
  • dependencies 可選參數(shù),是當(dāng)前模塊依賴的模塊名稱數(shù)組
  • factory 工廠方法,模塊初始化要執(zhí)行的函數(shù)或?qū)ο?,如果是對象,該對象為該模塊的輸出值,如果是函數(shù),則應(yīng)該只執(zhí)行一次。
    加載模塊使用require函數(shù)
    require([dependencies], function(){})
    require函數(shù)接收兩個參數(shù),前一個為一個數(shù)組,表示依賴的模塊,后一個為一個回調(diào)函數(shù),將在所有依賴加載成功后調(diào)用,加載的模塊會以參數(shù)的形式傳入該函數(shù),從而在該函數(shù)內(nèi)部可以使用這些模塊。由于require函數(shù)在加載依賴的時候是異步加載,這樣瀏覽器不會失去響應(yīng),并且會等所有依賴加載成功后再執(zhí)行回調(diào)函數(shù),解決了依賴性問題。

CMD

另一種規(guī)范叫做CMD(Common Module Definition),通用模板定義,是由國內(nèi)發(fā)展而來。就如AMD有一個requireJS,CMD用的是seaJS。seaJS要解決的問題和requireJS一樣,只是在模塊定義方式和模塊加載時機上有所不同。
CMD規(guī)范中,一個模塊就是一個文件,寫法為:
define(factory)
factory為函數(shù)時,表示模塊的構(gòu)造方法。執(zhí)行該構(gòu)造方法,可以得到模塊向外提供的接口。factory有三個參數(shù):
function(require,exports,module)

  • require是一個方法,require(id)接收模塊標識作為唯一參數(shù),用來獲取其他模塊提供的接口。
  • exports是一個對象,用來向外提供模塊接口。
  • module是一個對象,該對象上存儲了與當(dāng)前模塊相關(guān)聯(lián)的一些屬性和方法。
//定義模塊module.js
define(function(require,exports,module){
  var a = require('./a')
  a.dosomething
  //some code
  var b = require('./b')
  b.dosomething
  //some code
}
//加載模塊
seajs.use(['module.js'],function(){
// do something
});

從上述代碼可以看出,CMD與AMD的最大的不同點在于CMD推崇依賴就近,即只有在用到某個模塊的時候再去require,而AMD推崇依賴前置,在定義模塊的時候就要聲明其依賴。
同樣是異步加載 ,AMD會在加載模塊完成后就執(zhí)行該模塊,所有模塊加載執(zhí)行完后進入require的回調(diào)函數(shù),執(zhí)行主邏輯。所以依賴模塊的執(zhí)行順序和書寫順序不一定一致,哪個先下載,哪個先執(zhí)行。而CMD加載完某個依賴模塊后并不執(zhí)行,只是下載而已,在所有依賴模塊加載完成后,進入主邏輯,遇到require語句的時候才執(zhí)行對應(yīng)的模塊,所以模塊的執(zhí)行順序和書寫順序是完全一致的。

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

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

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