概念
1、為什么要使用模塊化?
- 當(dāng)代碼規(guī)模較大或進(jìn)行團(tuán)隊(duì)協(xié)作時,如果不實(shí)行模塊化,有可能導(dǎo)致命名沖突----解決命名沖突;
- 不使用模塊化的依賴調(diào)用比較容易陷入混亂,使用模塊化有助于依賴的管理----對依賴進(jìn)行管理;
- 模塊化的代碼更利于他人閱讀和協(xié)同開發(fā)維護(hù)----提高代碼可讀性;
- 方便代碼的復(fù)用----代碼解耦,提高復(fù)用性;
2、CMD、AMD、CommonJS 規(guī)范分別指什么?有哪些應(yīng)用
2.1、CMD(Common Module Definition)通用模塊定義規(guī)范
是瀏覽器端的模塊化規(guī)范,是在SeaJS的推廣過程中產(chǎn)生的,該規(guī)范明確了模塊的基本書寫格式和基本交互規(guī)則,提倡按需加載。
在CMD規(guī)范中,一個模塊就是一個文件,代碼格式:
define(id?, deps?, factory)
因?yàn)镃MD推崇:
1、一個文件一個模塊,所以經(jīng)常就用文件名作為模塊id;
2、CMD推崇依賴就近,所以一般不在define
的參數(shù)中寫依賴;
-
define
是一個全局函數(shù),用來定義模塊; - factory參數(shù)可以是一個函數(shù),也可以是一個對象或字符串。
- 當(dāng) factory為對象、字符串時,表示模塊的接口就是該對象、字符串。
- 當(dāng)factory為函數(shù)時表示是模塊的構(gòu)造方法。執(zhí)行該構(gòu)造方法可以得到模塊向外提供的接口。factory方法在執(zhí)行時,默認(rèn)會傳入三個參數(shù):require(把其他模塊導(dǎo)入)、exports(把模塊內(nèi)的一些屬性和方法導(dǎo)出) 和 module。
AMD推崇依賴前置,在定義模塊的時候就要聲明其依賴的模塊;
CMD推崇就近依賴,只有在用到某個模塊的時候再去require;
//CMD
define(function(require, exports, module){
var a = require('a');
a.doSomething();
var b = require('b');
b.doSomething(); // 依賴就近,延遲執(zhí)行
});
//AMD
define(['a', 'b'], function(a, b){ // 依賴前置,提前執(zhí)行
a.doSomething();
b.doSomething();
});
明顯看出和 AMD 不同,模塊定義時已不用立馬引入依賴,而是運(yùn)行到需要時候再加載,根據(jù)順序執(zhí)行,這樣更像是 CommonJS 的風(fēng)格,讓人感覺也像是同步加載似的。但實(shí)際上 CMD 內(nèi)部處理是對文件做了一個詞法的解析,在還沒執(zhí)行的時候,解析出所需的依賴,并不是真正的同步。
- CMD規(guī)范的主要應(yīng)用是SeaJS,現(xiàn)在已經(jīng)被廢棄。
2.2、AMD(Asynchronous Module Definition)異步模塊定義規(guī)范
它采用異步方式加載模塊,模塊的加載不影響它后面語句的運(yùn)行。所有依賴這個模塊的語句,都定義在一個回調(diào)函數(shù)中,等到加載完成之后,這個回調(diào)函數(shù)才會運(yùn)行。
由于不是JS原生支持,使用AMD規(guī)范進(jìn)行頁面開發(fā)需要用到對應(yīng)的函數(shù)庫,也就是大名鼎鼎的
RequireJS
,實(shí)際上AMD是RequireJS
在推廣過程中對模塊定義的規(guī)范化的產(chǎn)出。RequireJS
主要解決兩個問題
1、多個js文件可能有依賴關(guān)系,被依賴的文件需要早于依賴它的文件加載到瀏覽器;
2、js加載的時候?yàn)g覽器會停止頁面渲染,加載文件越多,頁面失去響應(yīng)時間越長;例子:
定義模塊myModule.js
// 定義模塊 myModule.js
define(['dependency'], function(){
var name = 'Byron';
function printName(){
console.log(name);
}
return {
printName: printName
};
});
加載模塊
require(['myModule'], function (my){
my.printName();
});
- 語法
1、RequireJS
定義了一個函數(shù) define
,它是全局變量,用來定義模塊
define(id?, dependencies?, factory);
- id:可選參數(shù),用來定義模塊的標(biāo)識,如果沒有提供該參數(shù),默認(rèn)就是腳本文件名(去掉拓展名);
- dependencies:是一個當(dāng)前模塊依賴的模塊名稱數(shù)組;
- factory:工廠方法,模塊初始化要執(zhí)行的函數(shù)或?qū)ο?。如果為函?shù),它應(yīng)該只被執(zhí)行一次。如果是對象,此對象應(yīng)該為模塊的輸出值;
2、在頁面上采用require
函數(shù)加載模塊
require([module], callback);
- 第一個參數(shù)
[module]
,是一個數(shù)組,里面的成員就是要加載的模塊; - 第二個參數(shù)
callback
,則是加載成功之后的回調(diào)函數(shù)。
加載的模塊會以參數(shù)形式傳入該函數(shù),從而在回調(diào)函數(shù)內(nèi)部就可以使用這些模塊;
require()
函數(shù)在加載依賴的函數(shù)的時候是異步加載的,這樣瀏覽器不會失去響應(yīng),它指定的回調(diào)函數(shù),只有前面的模塊都加載成功后,才會運(yùn)行,解決了依賴性的問題。
require(['math'], function(math){
math.add(2, 3);
});
math.add()
與math模塊加載不是同步的,瀏覽器不會發(fā)生假死。所以很顯然,AMD比較適合瀏覽器環(huán)境。
- 實(shí)現(xiàn)AMD 規(guī)范的庫有:
require.js、curl.js
和Dojo.js
等。
3.3、CommonJS通用模塊定義規(guī)范
CommonJS 由首先使用 js模塊化概念的 Node.js采用,是服務(wù)器端模塊的規(guī)范。
1、定義模塊
根據(jù)CommonJS規(guī)范,一個單獨(dú)的文件就是一個模塊。每一個模塊都是單獨(dú)的作用域,也就是說,在該模塊內(nèi)部定義的變量,無法被其他模塊讀取,除非定義為global對象的屬性。
2、模塊輸出
模塊只有一個出口,module.exports
對象我們需要把模塊希望輸出的內(nèi)容放入該對象。
3、加載模塊
加載模塊使用require
方法,該方法讀取一個文件并執(zhí)行,返回文件內(nèi)部的module.exports
對象。
例子:
模塊定義 myModel.js
//模塊定義 myModel.js
var name = 'Byron';
function printName(){
console.log(name);
}
function printFullName(firstName){
console.log(firstName + name);
}
module.exports = { //模塊輸出
printName: printName,
printFullName: printFullName
}
加載模塊
var nameModule = require('./myModel.js');
nameModule .printName();
不同的實(shí)現(xiàn)對require
時的路徑有不同要求,一般情況可以省略js拓展名,可以使用相對路徑,也可以使用絕對路徑,甚至可以省略路徑直接使用模塊名(前提是該模塊是系統(tǒng)內(nèi)置模塊)。