js 模塊化CommonJS AMD UMD CMD ES6規(guī)范詳解
js 的演變
通過(guò)這篇文章,筆者將介紹幾種 js 模塊化的規(guī)范,以及它們各自的優(yōu)缺點(diǎn)和差異......
在 js 剛出現(xiàn)的時(shí)候,只是作為腳本語(yǔ)言,但隨著瀏覽器的不斷發(fā)展,js 越來(lái)越被重視起來(lái),可以實(shí)現(xiàn)較為復(fù)雜的功能。這個(gè)時(shí)候開(kāi)發(fā)者為了維護(hù)方便,會(huì)把不同功能的模塊抽離出來(lái)寫(xiě)入單獨(dú)的 js 文件,但是當(dāng)項(xiàng)目更為復(fù)雜的時(shí)候,html 中可能會(huì)引入很多個(gè) js 文件,而這個(gè)時(shí)候就會(huì)出現(xiàn)命名沖突,污染作用域等一系列問(wèn)題,這個(gè)時(shí)候 模塊化 的概念及實(shí)現(xiàn)方法應(yīng)運(yùn)而生。
模塊化
模塊化開(kāi)發(fā)是一種管理方式,一種生產(chǎn)方式,也是一種解決問(wèn)題的方案。一個(gè)模塊就是實(shí)現(xiàn)某個(gè)特定功能的文件,我們可以很方便的使用別人的代碼,想要什么模塊,就引入那個(gè)模塊。但是模塊開(kāi)發(fā)要遵循一定的規(guī)范,后面就出現(xiàn)了我們所熟悉的一系列規(guī)范。
1. CommonJS 規(guī)范
CommonJS
主要用在 node
開(kāi)發(fā)上,每個(gè)文件就是一個(gè)模塊,每個(gè)文件都有自己的一個(gè)作用域。通過(guò)module.exports
暴露 public
成員。
此外,CommonJS
通過(guò) require
引入模塊依賴,require
函數(shù)可以引入 node
的內(nèi)置模塊、自定義模塊和 npm
等第三方模塊。
定義模塊:
// 定義模塊 math.js var basicNum = 0; function add(a, b) { return a + b; } // 在這里寫(xiě)上需要向外暴露的函數(shù)、變量 module.exports = { add: add, basicNum: basicNum }
加載模塊:
// 引入 math.js 模塊 var math = require('./math'); math.add(2, 3); // 5
優(yōu)點(diǎn):
- 簡(jiǎn)單并且容易使用
- 服務(wù)器端模塊便于重用
缺點(diǎn):
- 同步的模塊加載方式不適合在瀏覽器環(huán)境中
- 不能非阻塞的并行加載多個(gè)模塊
2. AMD 規(guī)范
AMD
是 (Asynchronous Module Definition) 的縮寫(xiě),意思就是"異步模塊定義"。它采用異步方式加載模塊,模塊的加載不影響它后面語(yǔ)句的運(yùn)行。所有依賴這個(gè)模塊的語(yǔ)句,都定義在一個(gè)回調(diào)函數(shù)中,等到加載完成之后,這個(gè)回調(diào)函數(shù)才會(huì)運(yùn)行。
在 AMD
規(guī)范中,我們使用 define
定義模塊,使用 require
加載模塊,但是不同于 CommonJS
,它要求兩個(gè)參數(shù):
定義模塊:
define(id?, dependencies?, factory);
id
是定義的模塊名,這個(gè)參數(shù)是 可選的,如果沒(méi)有定義該參數(shù),模塊名字應(yīng)該默認(rèn)為模塊加載器請(qǐng)求的指定腳本的名字,如果有該參數(shù),模塊名必須是頂級(jí)的絕對(duì)的。dependencies
是定義的模塊中所依賴的 模塊數(shù)組,也是 可選的,依賴模塊優(yōu)先級(jí)執(zhí)行,并且執(zhí)行結(jié)果按照數(shù)組中的排序依次以參數(shù)的形式傳入factory
。factory
是模塊初始化要執(zhí)行的函數(shù)或?qū)ο螅?必需的。
加載模塊:
require([module], callback);
第一個(gè)參數(shù) module
,是一個(gè)數(shù)組,里面的成員就是要加載的模塊;第二個(gè)參數(shù) callback
,則是加載成功之后的回調(diào)函數(shù)。如果將前面的 CommonJS
改寫(xiě)成 AMD
形式,就是下面這樣:
require(['./math'], function (math) { math.add(2, 3); });
優(yōu)點(diǎn):
- 適合在瀏覽器環(huán)境中異步加載模塊
- 可以并行加載多個(gè)模塊
缺點(diǎn):
- 提高了開(kāi)發(fā)成本
- 不符合通用的模塊化思維方式
3. UMD 規(guī)范
UMD
是 (Universal Module Definition) 通用模塊定義 的縮寫(xiě)。UMD
是 AMD
和 CommonJS
的一個(gè)糅合。AMD
是瀏覽器優(yōu)先,異步加載;CommonJS
是服務(wù)器優(yōu)先,同步加載。
既然要通用,怎么辦呢?那就先判斷是否支持 node
的模塊,支持就使用 node
;再判斷是否支持 AMD
,支持則使用 AMD
的方式加載。這就是所謂的 UMD
。
示例:
(function (window, factory) { if (typeof exports === "object") { // CommonJS module.exports = factory(); } else if (typeof define === "function" && define.amd) { // AMD define(factory); } else { // 瀏覽器全局定義 window.eventUtil = factory(); } })(this, function () { // do something });
4. CMD 規(guī)范
CMD
是 (Common Module Definition) 公共模塊定義 的縮寫(xiě)。CMD
可能是在 CommonJS
之后抽象出來(lái)的一套模塊化語(yǔ)法定義和使用的標(biāo)準(zhǔn)。
在 CMD
規(guī)范中,一個(gè)模塊就是一個(gè)文件。
定義模塊:
define(factory);
define
接收 factory
參數(shù),它可以是一個(gè)函數(shù),也可以是一個(gè)對(duì)象或一個(gè)字符串。
- 當(dāng)
factory
是一個(gè)對(duì)象或是一個(gè)字符串時(shí),表示該模塊的接口就是這個(gè)對(duì)象或者字符串。 - 當(dāng)
factory
是一個(gè)函數(shù)時(shí),表示是該模塊的構(gòu)造方法。執(zhí)行該構(gòu)造方法,可以得到模塊向外提供的接口。factory
在執(zhí)行時(shí),默認(rèn)傳入三個(gè)參數(shù):require
、exports
、module
。其中require
用來(lái)加載其它模塊,exports
用來(lái)向外提供模塊接口。module
是一個(gè)對(duì)象,存儲(chǔ)著與當(dāng)前模塊相關(guān)聯(lián)的一些屬性和方法。
加載模塊:
我們可以通過(guò) SeaJs
的 use
方法加載模塊:
seajs.use([module], callback);
優(yōu)點(diǎn):可以很容易在 node
中運(yùn)行
缺點(diǎn):依賴 SPM
打包,模塊的加載邏輯偏重
5. ES6 模塊化
ES6
模塊的設(shè)計(jì)思想是盡量的 靜態(tài)化,使得編譯時(shí)就能確定模塊的依賴關(guān)系,以及輸入和輸出的變量。CommonJS
和 AMD
模塊,都只能在運(yùn)行時(shí)確定這些東西。
在 ES6
中,我們使用 export
關(guān)鍵字來(lái)導(dǎo)出模塊,使用 import
關(guān)鍵字來(lái)引入模塊。
引入模塊:
// ES6模塊 import { stat, exists, readFile } from 'fs';
上面代碼實(shí)質(zhì)是從 fs
模塊中加載 3 個(gè)方法,其他方法不加載。這種加載稱為 “編譯時(shí)加載” 或者 靜態(tài)加載,即 ES6
可以在編譯時(shí)就完成模塊加載,效率要比 CommonJS
模塊的加載方式高。當(dāng)然,這也導(dǎo)致了沒(méi)法引用 ES6
模塊本身,因?yàn)樗皇菍?duì)象。
導(dǎo)出模塊:
let firstName = 'Zhou'; let lastName = 'ShuYi'; let year = 1994; export { firstName, lastName, year };
上面代碼在 export
后面,使用大括號(hào)指定所要輸出的一組變量。export
除了輸出變量,還可以輸出函數(shù)或類。
優(yōu)點(diǎn):容易進(jìn)行靜態(tài)分析
缺點(diǎn):原生瀏覽器端還沒(méi)有實(shí)現(xiàn)該標(biāo)準(zhǔn)
AMD 和 CMD 的區(qū)別
對(duì)于依賴的模塊,AMD
是 提前執(zhí)行,CMD
是 延遲執(zhí)行。
AMD
推崇 依賴前置,CMD
推崇 依賴就近。
AMD
的 API 默認(rèn)是一個(gè)當(dāng)多個(gè)用,CMD
的 API 嚴(yán)格區(qū)分,推崇職責(zé)單一。
ES6 模塊與 CommonJS 模塊的差異
CommonJS
模塊輸出的是一個(gè) 值的拷貝,ES6
模塊輸出的是 值的引用。
CommonJS
模塊是運(yùn)行時(shí)加載,ES6
模塊是編譯時(shí)輸出接口。
CommonJS
模塊的 require()
是 同步加載 模塊,ES6
模塊的 import
命令是 異步加載,有一個(gè)獨(dú)立的模塊依賴的解析階段。
最后
以上就是筆者對(duì)于 js 模塊化的一些理解,更多關(guān)于js 模塊化規(guī)范的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
javascript 原型與原型鏈的理解及實(shí)例分析
這篇文章主要介紹了javascript 原型與原型鏈的理解,結(jié)合實(shí)例形式分析了javascript 原型與原型鏈的原理、使用方法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2019-11-11javascript實(shí)現(xiàn)tab切換的兩個(gè)實(shí)例
這篇文章主要介紹了javascript實(shí)現(xiàn)tab切換的兩個(gè)實(shí)例,是對(duì)之前方法原理的進(jìn)一步延伸,需要深入了解的同學(xué)可以參考一下2015-11-11javascript 在firebug調(diào)試時(shí)用console.log的方法
當(dāng)你使用console.log()函數(shù)時(shí),下面的firebug一定要打開(kāi),不然這函數(shù)在用firefox運(yùn)行時(shí)無(wú)效且影響正常程序,如果用IE打開(kāi),將會(huì)出錯(cuò)2012-05-05JS 通過(guò)系統(tǒng)時(shí)間限定動(dòng)態(tài)添加 select option的實(shí)例代碼
這篇文章主要介紹了JS 通過(guò)系統(tǒng)時(shí)間限定 動(dòng)態(tài)添加 select option的實(shí)例代碼,非常不錯(cuò)具有參考借鑒價(jià)值,需要的朋友可以參考下2016-06-06Javascript異步編程的4種方法讓你寫(xiě)出更出色的程序
本文總結(jié)了"異步模式"編程的4種方法,理解它們可以讓你寫(xiě)出結(jié)構(gòu)更合理、性能更出色、維護(hù)更方便的Javascript程序2013-01-01JS數(shù)組求和的幾種常見(jiàn)方法總結(jié)
js的數(shù)組與我們?nèi)粘I钪械臄?shù)組一樣,都是會(huì)進(jìn)行求和計(jì)算的,下面這篇文章主要給大家介紹了關(guān)于JS數(shù)組求和的幾種常見(jiàn)方法,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-01-01編寫(xiě)跨瀏覽器的javascript代碼必備[js多瀏覽器兼容寫(xiě)法]
下面比較了幾種瀏覽器之間的差異,在寫(xiě)javascript代碼時(shí) 要時(shí)刻注意這些差異2008-10-10