學(xué)習(xí)一門技術(shù)孩等,基礎(chǔ)是關(guān)鍵艾君,比如想學(xué)react或redux,那就得有node和webpack的基礎(chǔ)肄方;學(xué)習(xí)webpack冰垄,知道commonjs規(guī)范,理解起來才會(huì)更快 权她。所謂萬丈高樓平地起虹茶,基礎(chǔ)打牢了,理解知識(shí)點(diǎn)才會(huì)更透徹隅要,后期提升才會(huì)更快蝴罪。
nodejs和commonjs區(qū)別?
commonjs是規(guī)范步清,nodejs是這種規(guī)范的實(shí)現(xiàn)要门。
commonjs還在不斷的發(fā)展,還會(huì)有新的東西往里添加廓啊。
CommonJs有很多實(shí)現(xiàn)欢搜,其中不乏大名鼎鼎的項(xiàng)目,比如說Apache的CouchDB和node.js
這些項(xiàng)目大部分只實(shí)現(xiàn)CommonJs的部分規(guī)范谴轮。
CommonJs概述
每個(gè)文件就一個(gè)模塊狂巢,有自己的作用域。在一個(gè)文件里面定義的變量书聚、函數(shù)唧领、類,都是私有的雌续,對(duì)其他文件不可見斩个。
私有作用域不會(huì)污染全局作用域。
模塊可加載多次驯杜,只會(huì)在第一次加載時(shí)運(yùn)行一次受啥,然后結(jié)果會(huì)被緩存起來,以后在使用鸽心,就直接讀取緩存結(jié)果滚局。想要讓模塊再次運(yùn)行,必須清除緩存
模塊加載順序是按照其在代碼中出現(xiàn)的順序
基本使用
1顽频、輸出:module對(duì)象藤肢。
使用module.exports輸出一個(gè)變量或函數(shù),也可以使用exports輸出
var x = 5;
var addX = function (value) {
return value + x;
};
module.exports.x = x;
module.exports.addX = addX;
module是可以省略的糯景。
2嘁圈、module對(duì)象
node內(nèi)部提供一個(gè)module構(gòu)建函數(shù)。每個(gè)模塊內(nèi)部蟀淮,都有一個(gè)module對(duì)象最住,代表當(dāng)前模塊,他有一些自身屬性
module.id 模塊的識(shí)別符怠惶,通常是帶有絕對(duì)路徑的模塊文件名涨缚。
module.filename 模塊的文件名,帶有絕對(duì)路徑策治。
module.loaded 返回一個(gè)布爾值脓魏,表示模塊是否已經(jīng)完成加載。
module.parent 返回一個(gè)對(duì)象览妖,表示調(diào)用該模塊的模塊轧拄。
module.children 返回一個(gè)數(shù)組,表示該模塊要用到的其他模塊讽膏。
module.exports 表示模塊對(duì)外輸出的值檩电。
3、exports變量
node為么每個(gè)模塊提供一個(gè)exports變量府树,指向module.exports
exports.area = function(r){console.log(r)}
這個(gè)是ok的俐末。
exports = function(r){console.log(r)}
這個(gè)寫法無效,因?yàn)楦淖兞烁淖兞薳xports的指向
如果一個(gè)文件中 module.exports 被重新賦值奄侠,該文件中其他exports出去的變量(屬性)也是無效的晴楔。
說說加載
上面說了輸出(module.exoprts、exports),接下來說說載入巷挥。
node采用CommonJS規(guī)范。內(nèi)置require命令用于加載模塊:var foo = require("filename")
加載規(guī)則
說說require內(nèi)部處理流程
1闷盔、在加載前,檢查是否有緩存(module._cache)
2旅急、如果緩存之中沒有逢勾,就創(chuàng)建一個(gè)新的module實(shí)例
3、將他緩存起來
4藐吮、使用module.load()加載指定模塊文件溺拱;讀取文件內(nèi)容之后,使用module.compile()執(zhí)行文件代碼
5谣辞、如果加載/解析過程報(bào)錯(cuò)迫摔,就從緩存刪除該模塊
6、返回該模塊的module.exports
第一步是加載文件的規(guī)則
一般后綴默認(rèn)為.js
路徑分三種情況
1泥从、('/home/app.js') -->絕對(duì)路徑
2句占、('./home/app.js') -->相對(duì)路徑 (當(dāng)前路徑下的home文件夾下的appjs文件)
3、('react') --> 沒有 以 ‘./ 和 /’開頭的路徑歉闰,則表示加載的一個(gè)默認(rèn)提供的核心模塊辖众,會(huì)去node_modules文件夾下找或node的系統(tǒng)安裝目錄中找
如果指定的文件沒有發(fā)現(xiàn),node會(huì)嘗試為文件名添加.js .json .node后綴和敬,再搜索凹炸。.js文件會(huì)以文本格式的js腳本文件解析,.json文件會(huì)以JSON格式的文本文件解析昼弟,.node文件會(huì)以編譯后的二進(jìn)制文件解析啤它。
模塊緩存
第一次加載某個(gè)模塊,node會(huì)緩存該模塊舱痘,以后再次加載會(huì)直接從緩存中取出該模塊的module.exports屬性
刪除執(zhí)行模塊
delete require.cache[moduleName]
AMD規(guī)范與CommonJS的區(qū)別
CommonJS加載模塊是同步的变骡,加載完才執(zhí)行后續(xù)操作;
AMD是異步加載模塊芭逝,允許指定回調(diào)函數(shù)塌碌。
nodejs用于服務(wù)器端,代碼文件一般都在本地硬盤旬盯,加載比較快台妆,適合使用CommonJS,而瀏覽器環(huán)境胖翰,要從服務(wù)器加載模塊接剩,比較慢,所以瀏覽器一般采用AMD規(guī)范萨咳。
為什么會(huì)有規(guī)范懊缺?
回答這個(gè)問題之前得知道為什么要有模塊?
因?yàn)橛辛四K,我們就可以很方便的使用別人的代碼培他,想要什么功能鹃两,就加載什么模塊遗座,這樣帶來了一個(gè)問題大家得按照相應(yīng)的方式編寫模塊,不然每個(gè)人寫自己風(fēng)格的代碼俊扳,豈不是亂套了员萍。
所有才有了CommonJS、AMD拣度、CMD這些規(guī)范!
再說說AMD規(guī)范
全稱:異步模塊定義
誕生背景:基于commonJS規(guī)范的nodeJS出來以后螃壤,服務(wù)端的模塊概念已經(jīng)形成抗果,很自然地,大家就想要客戶端模塊奸晴。而且最好兩者能夠兼容冤馏,一個(gè)模塊不用修改,在服務(wù)器和瀏覽器都可以運(yùn)行寄啼。但是逮光,由于一個(gè)重大的局限,使得CommonJS規(guī)范不適用于瀏覽器環(huán)境墩划。
同步加載對(duì)服務(wù)器端不是一個(gè)問題涕刚,因?yàn)樗械哪K都存放在本地硬盤,可以同步加載完成乙帮,等待時(shí)間就是硬盤的讀取時(shí)間杜漠。但是,對(duì)于瀏覽器察净,這卻是一個(gè)大問題驾茴,因?yàn)槟K都放在服務(wù)器端,等待時(shí)間取決于網(wǎng)速的快慢氢卡,可能要等很長(zhǎng)時(shí)間锈至,瀏覽器處于"假死"狀態(tài)。
因此译秦,瀏覽器端的模塊峡捡,不能采用"同步加載"(synchronous),只能采用"異步加載"(asynchronous)诀浪。這就是AMD規(guī)范誕生的背景棋返。
AMD實(shí)現(xiàn)庫有 require.js和curl.js。
requirejs主要解決兩個(gè)問題:
1雷猪、實(shí)現(xiàn)js文件的異步加載睛竣,避免頁面因?yàn)閖s文件加載失去響應(yīng)
2、管理模塊之間的依賴性求摇,便于代碼的編寫和維護(hù)
AMD模塊的寫法
模塊必須使用define函數(shù)來定義射沟。如果一個(gè)模塊不依賴其他模塊殊者,那么可以直接定義在define()函數(shù)之中。
例子:
// 沒有依賴其他模塊
//math.js
define(function (){
var add = function (x,y){
return x+y;
};
return {
add: add
};
});
// 加載方法(這是沒有)
require(['math'], function (math){
alert(math.add(1,1));
});
//如果依賴其他模塊
define(['myLib'], function(myLib){
function foo(){
myLib.doSomething();
}
return {
foo : foo
};
});
// 會(huì)先加載myLib模塊
還可以加載非規(guī)范的模塊
使用require.config方法验夯,定義他們的一些特征
例子:
// underscore和backbone這兩個(gè)庫猖吴,都沒有采用AMD規(guī)范編寫。如果要加載它們的話挥转,必須先定義它們的特征海蔽。
require.config({
shim: {
'underscore':{
exports: '_'
},
'backbone': {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
}
}
});
另外還有CMD,玉伯寫的seajs绑谣,就是遵循他提出的CMD規(guī)范党窜。
總結(jié)
為什么要有規(guī)范?
因?yàn)橐K化借宵。為什么要模塊化幌衣?
因?yàn)楝F(xiàn)在項(xiàng)目越來越大,按照功能劃分壤玫,使用的時(shí)候直接加載相應(yīng)的模塊即可豁护,利于維護(hù)和開發(fā)。比如nodejs都可以做服務(wù)器端語言了欲间,將來項(xiàng)目肯定會(huì)越來越大楚里,越來越復(fù)雜(個(gè)人認(rèn)為:node將來會(huì)向java、Python這個(gè)方向發(fā)展的)括改。項(xiàng)目劃分清晰明了為什么現(xiàn)在有那么多規(guī)范腻豌?(CommonJS、AMD嘱能、CMD)
因?yàn)椴煌?guī)范適用于不同的場(chǎng)景吝梅。
CommonJS是同步加載,很快能得到結(jié)果的可以使用這個(gè)方式惹骂。比如:node加載文件時(shí)是在本地硬盤加載的苏携,肯定快。
AMD对粪、CMD是異步加載右冻,獲取結(jié)果比較慢的的情況可以使用這種方式,因?yàn)樗梢栽O(shè)置回調(diào)著拭,什么時(shí)候加載完成纱扭,什么時(shí)候執(zhí)行對(duì)調(diào)。比如:瀏覽器加載服務(wù)器的數(shù)據(jù)儡遮,這個(gè)受網(wǎng)絡(luò)環(huán)境影響很大乳蛾,不能一直等服務(wù)器返回結(jié)果。所以可以給個(gè)回調(diào)方法,什么時(shí)候有結(jié)果了肃叶,走回調(diào)即可蹂随。
第一次寫這么長(zhǎng)的文章,肯定有很多值得商榷的地方因惭,以后好好努力
參考文獻(xiàn):
什么是CommonJS? - 依水間 - 博客園
js模塊化編程之徹底弄懂CommonJS和AMD/CMD!
CommonJS規(guī)范 -- JavaScript 標(biāo)準(zhǔn)參考教程(alpha)