一、簡介
1.什么是模塊化舌稀?
簡單地說聂儒,模塊化就是有組織地把一個(gè)大文件拆成獨(dú)立并互相依賴的多個(gè)小模塊扩所。
模塊內(nèi)部有許多私有屬性围详,只向外暴露一部分公開的接口(如可以修改私有屬性的方法等)
2.為什么要模塊化?
ES6之前祖屏,JavaScript語言一直沒有模塊(module)體系助赞,無法把大文件有組織地劃分成小塊,并管理之間地依賴袁勺。但是模塊化的思想一直存在雹食。因?yàn)橛肑avascript寫的代碼越來越龐大,而網(wǎng)頁也越來越像桌面APP期丰。如此龐雜的代碼群叶,如果不進(jìn)行模塊化,就可能引發(fā)命名沖突钝荡,造成不易復(fù)用街立、維護(hù)性高。
3.模塊劃分
一般埠通,為了清晰明了赎离,一個(gè)文件對(duì)應(yīng)一個(gè)模塊。
二端辱、模塊化規(guī)范
1. CommonJS
Node.js遵循的規(guī)范
const { PI } = Math;
module.exports.getArea = (r) => PI * r * 2; // 向外暴露(計(jì)算圓形的面積的)getArea方法
注意梁剔,這里的module可省略(不推薦)虽画,寫成:
const { PI } = Math
exports.getArea = (r) => PI * r * 2 // 向外暴露(計(jì)算圓形的面積的)getArea方法(省略了module)
特點(diǎn):
1.代碼運(yùn)行在模塊作用域,不會(huì)污染全局
2.加載模塊順序按照詞法解析的順序加載
3.加載模塊是同步的
4.單例加載:也就是加載的模塊會(huì)緩存起來荣病,再次使用時(shí)码撰,會(huì)直接用運(yùn)行結(jié)果,不會(huì)再加載(除非手動(dòng)清除)
5.加載模塊得到的是結(jié)果的拷貝
2. AMD
RequireJS遵循的規(guī)范
AMD用define()定義模塊个盆,用require()加載模塊灸拍。
// 定義一個(gè)新的模塊,這個(gè)新的模塊可能用到a模塊和b模塊
// define第一個(gè)參數(shù)是 依賴模塊的數(shù)組砾省,對(duì)應(yīng)callback里的參數(shù)
define(["a", "b"], function(a, b) {
// 1. 在最前面加載了a鸡岗、b
// 2. 使用模塊a
const a = require('./a') // a在最前面已經(jīng)加載好了
// 3.處理其他代碼
if (false) {
// 這里實(shí)際上是不可達(dá)代碼(永遠(yuǎn)不會(huì)被執(zhí)行)
// 所以b實(shí)際不需要加載。然而因?yàn)锳MD是依賴前置编兄、提前執(zhí)行的轩性,
// 所以b還是會(huì)在最前面被提前加載的
console.log(b.getArea(1)) // 6.283185307179586
}
// 向外暴露接口(寫法1)
const getArea = (r) => PI * r * 2
return { getArea }
// 向外暴露接口(寫法2)
const getArea2 = (r) => PI * r * 2
exports.getArea2 = getArea2
});
特點(diǎn):
1.加載模塊推崇依賴前置、提前執(zhí)行
2.加載模塊是異步的
3. CMD
sea.js遵循的規(guī)范
CMD也是用define()定義模塊狠鸳,用require()加載模塊揣苏,但思想不同、寫法也不同件舵。
CMD規(guī)范是在sea.js推廣的過程中產(chǎn)生的卸察,寫法如下:
// 定義一個(gè)新的模塊,這個(gè)新的模塊可能用到a模塊和b模塊
// define接收一個(gè)callback參數(shù)铅祸,對(duì)應(yīng)
define(function(require, exports, module) {
// 最前面不提前加載模塊
// 1. 使用模塊a
const a = require('./a') // 依賴就近坑质、延遲執(zhí)行,需要a临梗,就去require a
// 2.處理其他代碼
if(false) {
// 這里實(shí)際上是不可達(dá)代碼(永遠(yuǎn)不會(huì)被執(zhí)行)
// 所以b實(shí)際不需要加載涡扼。恰好CMD是依賴就近、延遲執(zhí)行的盟庞,
// 所以b的確是不會(huì)被加載的
const b = require('./b')
// 處理其他代碼
}
// 向外暴露接口(寫法1)
const getArea = (r) => PI * r * 2
return { getArea }
// 向外暴露接口(寫法2)
const getArea2 = (r) => PI * r * 2
exports.getArea2 = getArea2
})
// 使用的地方:
seajs.use(['a.js'], function(a){ // 依賴就近吃沪,需要a,就去require a
console.log(a.getArea(1)) // 6.283185307179586
})
特點(diǎn):
1.加載模塊推崇依賴就近什猖、延遲執(zhí)行
2.加載模塊也是異步的
4. ES6 模塊
ES6 模塊的設(shè)計(jì)思想是盡量的靜態(tài)化票彪,使得編譯時(shí)就能確定模塊的依賴關(guān)系
// a.js
const getArea = (r) => PI * r * 2
export { getArea } // 向外暴露一個(gè)對(duì)象,對(duì)象有一個(gè)(計(jì)算圓形的面積的)方法getArea
// 使用的地方:
import { getArea } = './a.js' // 注意這里拿到的是一個(gè)對(duì)象不狮,對(duì)象里有g(shù)etArea方法
console.log(getArea(1)) // 6.283185307179586
ES6還提供了一個(gè)default降铸,用來提供默認(rèn)的export:
// a.js
const getArea = (r) => PI * r * 2
export default getArea // 向外直接暴露一個(gè)(計(jì)算圓形的面積的)方法getArea
// 用的地方:
import getArea = './a.js' // 注意這里拿到的就是getArea方法
console.log(getArea(1)) // 6.283185307179586
特點(diǎn):
1.編譯時(shí)確定模塊的依賴關(guān)系
2.加載模塊存儲(chǔ)的是值的引用,所以全局只有一份
3.加載模塊也是異步的
三荤傲、總結(jié)
最開始垮耳,JavaScript沒有模塊。后來:
有CommoneJS規(guī)范,最典型的實(shí)踐就是Node.js终佛,主要使用在服務(wù)器端俊嗽,同步加載模塊;
有AMD铃彰,最典型的實(shí)踐就是RequireJS绍豁,依賴前置,主要使用在瀏覽器端牙捉,異步加載模塊竹揍。
有CMD,最典型的實(shí)踐就是sea.js邪铲,依賴就近芬位,主要使用在瀏覽器端,異步加載模塊带到。
有ES6的Module昧碉,在語言層面定義了模塊,通過export和import揽惹,吸收了CommoneJS和AMD兩者的優(yōu)點(diǎn)被饿,兼容兩標(biāo)準(zhǔn)的規(guī)范
四、推薦文章:
Node.js Nodule
RequireJS-定義模塊
Sea.js-定義模塊
ES6 Module 的語法-阮一峰
(完)