如今JavaScript模塊化編程的概念已經(jīng)普及開來,一提起模塊化,大家想到的可能是AMD,CMD,requirejs或seajs。其實(shí)還有很多其他的概念。本文將會陳述下JavaScript模塊的前世今生。
眾所周知,JavaScript由于歷史的原因并沒有模塊的概念,自從ajax帶來了web2.0概念后,js代碼已經(jīng)和以前大不相同了,2009年HTML5興起后,前端代碼的行數(shù)已經(jīng)呈現(xiàn)井噴式發(fā)展,隨著代碼量的增加,模塊的缺失的缺點(diǎn)日益凸顯,Javascript社區(qū)做了很多探索。
模塊并非js語言獨(dú)創(chuàng),顯然是借鑒其他語言的,下面是百度百科對模塊的定義:
模塊,又稱構(gòu)件,是能夠單獨(dú)命名并獨(dú)立地完成一定功能的程序語句的集合(即程序代碼和數(shù)據(jù)結(jié)構(gòu)的集合體)
從中提煉出幾個關(guān)鍵字就是,獨(dú)立,集合,完成一定功能。
上面的提煉,再從其他語言的實(shí)現(xiàn)中借鑒下,總結(jié)起來,我們期待的模塊有如下特性:
其實(shí)我們想要的就是一個獨(dú)立的模塊,并能引用依賴,及被依賴。
C語言的庫和頭文件(include),java的包(import)。這在其他語言中都是原生支持的特性,在js中卻是沒有的。
如果僅從定義入手,那么一個函數(shù)即可成為一個模塊(獨(dú)立,集合,完成一個功能),那我們就先從最原始的探索開始,也許不經(jīng)意間,我們早已在使用模塊了。
//最簡單的函數(shù),可以稱作一個模塊
function add(x, y) {
return x + y;
}
稍微了解點(diǎn)javascript基礎(chǔ)的人都知道js中能創(chuàng)建作用域的就是函數(shù)(ES6之前),總結(jié)下社區(qū)的探索,對模塊的模擬大概如下:
(function (mod, $, _) {
mod.add = ***;
mod.sub = ***;
}((window.mod = window.mod || {}), jQuery, Underscore));
上面的mod模塊不會重復(fù)定義,可自由定義依賴。
99%的人思想會止步于此,但這種實(shí)現(xiàn)其實(shí)并不完美,仍然需要手動維護(hù)依賴的順序。典型的場景就是上面的jquery必須先于我們的代碼引入,不然會報(bào)引用錯誤,這顯然不是我們想要的。
我在寫Painter的時(shí)候,曾經(jīng)手動維護(hù)幾十個script之間的先后順序,這種感覺很虐心,最后想加個新script很容易報(bào)錯。下面介紹的
前段時(shí)間雅虎宣布YUI不再更新了,很是傷感,最早接觸模塊的概念,當(dāng)屬YUI了,當(dāng)然不是YUI2了。
YUI3經(jīng)過全新設(shè)計(jì),使用了沙箱模式 + 命名空間的方式,并有了模塊的概念。
例如在YUI3中想使用一個模塊,需要如下這樣:
//使用node模塊,node模塊會作為參數(shù)傳入
YUI().use('node', function (Y) {
///***
}
YUI的模塊化已經(jīng)做的很好了,但對于僅想使用模塊的人,要引入YUI確實(shí)有點(diǎn)太重了。
說道CMD就不能不提commonjs,提到commonjs就不能不提node。
CMD規(guī)范參照commonjs中的方式,定義模塊的方式如下:
define(function(require, exports, module) {
// The module code goes here
});
一個文件就是一個模塊,文件名就是模塊的名字,使用模塊的方法也和commonjs中一致,只需require就好了,模塊名字可省略后綴。
//使用event.js模塊
var ec = require('event');
CMD的典型實(shí)現(xiàn)就是seajs,應(yīng)用的很廣泛。
[AMD](https://github.com/amdjs/amdjs-api/wiki/AMD-(%E4%B8%AD%E6%96%87%E7%89%88)是異步模塊定義,特別適合在瀏覽器端使用,其規(guī)范和CMD是很像的,AMD規(guī)范中定義模塊的方式如下:
define(id?, dependencies?, factory);
同CMD一樣,一個文件即一個模塊,模塊的使用方法如下:
define(["beta"], function (beta) {
bata.***//調(diào)用模塊
});
AMD主張依賴注入,這點(diǎn)和CMD不同(以來查找)。
AMD也支持已CMD的方式來使用依賴。
AMD的典型實(shí)現(xiàn)有requireJS,modJS和lodJS。
KMD是kissy中提出來的,是kissy自己的一套模塊化方案,具體我也不是很清楚,感興趣的同學(xué)可自行搜索相關(guān)資料。
有一次同事@eric曦堯無意說起,KMD的意思是 kill amd and cmd,當(dāng)時(shí)覺得好高打上的名字(/ □ \)。
ES6帶來了語言層面的模塊化支持,規(guī)范方面見這里,文檔方面見這里。
我們現(xiàn)在期待的就是ES6規(guī)范快點(diǎn)塵埃落定(據(jù)說今年夏天),現(xiàn)在還處于草案狀態(tài),還有瀏覽器廠商們的大力支持,還有就是在國內(nèi)盡快普及開來。
UMD的全稱是Universal Module Definition。和它名字的意思一樣,這種規(guī)范基本上可以在任何一個模塊環(huán)境中工作。
一段典型的UMD代碼如下所示:
(function (root, factory) {
var Data = factory(root);
if ( typeof define === 'function' && define.amd) {
// AMD
define('data', function() {
return Data;
});
} else if ( typeof exports === 'object') {
// Node.js
module.exports = Data;
} else {
// Browser globals
var _Data = root.Data;
Data.noConflict = function () {
if (root.Data === Data) {
root.Data = _Data;
}
return Data;
};
root.Data = Data;
}
}(this, function (root) {
var Data = ...
//自己的代碼
return Data;
}));
這是出自data.js中的一部分代碼,其原理就是做個判斷,不同的環(huán)境進(jìn)行不同的處理。
我已將UMD應(yīng)用到自己的項(xiàng)目中,瞬間感覺高大上了不少:-)。
比較成氣候的模塊化方案,當(dāng)屬AMD和CMD,網(wǎng)上關(guān)于二者比較的文章甚多,很難評價(jià)誰好誰壞,當(dāng)下開來AMD的使用范圍似乎更廣些,而CMD的本土化方面做的更好。
這些模塊化的探索,使前端工程化成為了可能,可以說沒有模塊,工程化更無從彈起,本文總結(jié)了大家在模塊化方面的一些探索,下一篇文章將重點(diǎn)介紹下lodJS(一款基于AMD的模塊加載器)的實(shí)踐和原理。
更多建議: