最近幾年盟步,我們可以選擇的Javascript組件的生態(tài)系統(tǒng)一直在穩(wěn)步增長。雖然陡增的選擇范圍是極好的榜苫,但當(dāng)組件混合匹配使用時就會出現(xiàn)很尷尬的局面铝宵。開發(fā)新手們會很快發(fā)現(xiàn)不是所有組件都能彼此“和平相處”。
為了解決這個問題看成,兩種競爭關(guān)系的模塊規(guī)范AMD和CommonJS問世了君编,它們允許開發(fā)者遵照一種約定的沙箱化和模塊化的方式來寫代碼,這樣就能避免“污染生態(tài)系統(tǒng)”川慌。
Javascript模塊化
在了解這些規(guī)范之前吃嘿,還是先了解一下什么是模塊化。
模塊化是指在解決某一個復(fù)雜問題或者一系列的雜糅問題時梦重,依照一種分類的思維把問題進行系統(tǒng)性的分解以之處理兑燥。模塊化是一種處理復(fù)雜系統(tǒng)分解為代碼結(jié)構(gòu)更合理,可維護性更高的可管理的模塊的方式忍饰√吧可以想象一個巨大的系統(tǒng)代碼,被整合優(yōu)化分割成邏輯性很強的模塊時艾蓝,對于軟件是一種何等意義的存在力崇。對于軟件行業(yè)來說:解耦軟件系統(tǒng)的復(fù)雜性,使得不管多么大的系統(tǒng)赢织,也可以將管理亮靴,開發(fā),維護變得“有理可循”于置。
還有一些對于模塊化一些專業(yè)的定義為:模塊化是軟件系統(tǒng)的屬性茧吊,這個系統(tǒng)被分解為一組高內(nèi)聚,低耦合的模塊。那么在理想狀態(tài)下我們只需要完成自己部分的核心業(yè)務(wù)邏輯代碼搓侄,其他方面的依賴可以通過直接加載被人已經(jīng)寫好模塊進行使用即可瞄桨。
首先,既然是模塊化設(shè)計讶踪,那么作為一個模塊化系統(tǒng)所必須的能力:
- 定義封裝的模塊芯侥。
- 定義新模塊對其他模塊的依賴。
- 可對其他模塊的引入支持乳讥。
好了柱查,思想有了,那么總要有點什么來建立一個模塊化的規(guī)范制度吧云石,不然各式各樣的模塊加載方式只會將局?jǐn)嚨酶鼮榛靵y唉工。那么在JavaScript中出現(xiàn)了一些非傳統(tǒng)模塊開發(fā)方式的規(guī)范 CommonJS的模塊規(guī)范,AMD(Asynchronous Module Definition)汹忠,CMD(Common Module Definition)等淋硝。
CommonJS
CommonJS是服務(wù)器端模塊的規(guī)范,Node.js采用了這個規(guī)范错维。
根據(jù)CommonJS規(guī)范奖地,一個單獨的文件就是一個模塊。加載模塊使用require方法赋焕,該方法讀取一個文件并執(zhí)行参歹,最后返回文件內(nèi)部的exports對象。
CommonJS 有三個全局變量 module隆判、exports 和 require犬庇。但是由于 AMD 也有 require 這個全局變量,故不使用這個require變量來進行檢測侨嘀。
如果想要對外提供接口的話臭挽,可以將接口綁定到 exports (即 module.exports) 上。
function MyModule() {
// ...
}
if(typeof module !== `undefined` && typeof exports === `object`) {
module.exports = MyModule;
}
CommonJS 加載模塊是同步的咬腕,所以只有加載完成才能執(zhí)行后面的操作欢峰。像Node.js主要用于服務(wù)器的編程,加載的模塊文件一般都已經(jīng)存在本地硬盤涨共,所以加載起來比較快纽帖,不用考慮異步加載的方式,所以CommonJS規(guī)范比較適用举反。但如果是瀏覽器環(huán)境懊直,要從服務(wù)器加載模塊,這是就必須采用異步模式火鼻。所以就有了 AMD CMD 解決方案室囊。
AMD
AMD是"Asynchronous Module Definition"的縮寫雕崩,意思就是"異步模塊定義".
由于不是JavaScript原生支持,使用AMD規(guī)范進行頁面開發(fā)需要用到對應(yīng)的庫函數(shù)融撞,也就是大名鼎鼎RequireJS盼铁,實際上AMD 是 RequireJS 在推廣過程中對模塊定義的規(guī)范化的產(chǎn)出
AMD設(shè)計出一個簡潔的寫模塊API:
模塊定義define(id?, dependencies?, factory);
第一個參數(shù) id 為字符串類型,表示了模塊標(biāo)識懦铺,為可選參數(shù)捉貌。若不存在則模塊標(biāo)識應(yīng)該默認定義為在加載器中被請求腳本的標(biāo)識支鸡。如果存在冬念,那么模塊標(biāo)識必須為頂層的或者一個絕對的標(biāo)識。
第二個參數(shù)牧挣,dependencies 急前,是一個當(dāng)前模塊依賴的,已被模塊定義的模塊標(biāo)識的數(shù)組字面量瀑构。
第三個參數(shù)裆针,factory,是一個需要進行實例化的函數(shù)或者一個對象寺晌。
通過參數(shù)的排列組合世吨,這個簡單的API可以從容應(yīng)對各種各樣的應(yīng)用場景,如下所述呻征。
//3個參數(shù)耘婚,定義一個名為 ‘a(chǎn)lpha’ 的模塊,依賴于require陆赋,exports沐祷,beta這三個模塊
define("alpha", [ "require", "exports", "beta" ], function( require, exports, beta ){
export.verb = function(){
return beta.verb();
// or:
return require("beta").verb();
}
});
//2個參數(shù),定義一個匿名模塊攒岛,依賴于require赖临,exports,beta這三個模塊
define([ "require", "exports", "beta" ], function( require, exports, beta ){
export.verb = function(){
return beta.verb();
// or:
return require("beta").verb();
}
});
//1個參數(shù),定義一個匿名模塊灾锯,不依賴其他模塊
define(function(){
return 'hello world';
});
也可定義定義數(shù)據(jù)對象模塊
//1個參數(shù),定義一個定義數(shù)據(jù)對象模塊
define({
users: [],
members: []
});
//等價于
define(function(){
return{
users: [],
members: []
}
});
除了define外兢榨,AMD還保留一個關(guān)鍵字require。require 作為規(guī)范保留的全局標(biāo)識符顺饮,可以實現(xiàn)為 module loader吵聪,也可以不實現(xiàn)。
模塊加載require([module], callback)
require(['math'], function(math) {
math.add(2, 3);
});
AMD 規(guī)范中领突,define 函數(shù)同樣有一個公有屬性 define.amd暖璧。
AMD 中的參數(shù)便是這個模塊的依賴。那么如何在 AMD 中提供接口呢君旦?它是返回一個對象澎办,這個對象就作為這個模塊的接口嘲碱,故我們可以這樣寫:
function MyModule() {
// ...
}
if(typeof define === `function` && define.amd) {
define(function() { return MyModule; });
}
CMD
CMD 即Common Module Definition通用模塊定義,CMD規(guī)范是國內(nèi)發(fā)展出來的局蚀,就像AMD有個requireJS麦锯,CMD有個瀏覽器的實現(xiàn)SeaJS,SeaJS要解決的問題和requireJS一樣琅绅,只不過在模塊定義方式和模塊加載(可以說運行扶欣、解析)時機上有所不同
語法
Sea.js 推崇一個模塊一個文件,遵循統(tǒng)一的寫法
define(id?, deps?, factory)
因為CMD推崇
- 一個文件一個模塊千扶,所以經(jīng)常就用文件名作為模塊id
- CMD推崇依賴就近料祠,所以一般不在define的參數(shù)中寫依賴,在factory中寫
factory是一個函數(shù)澎羞,有三個參數(shù)髓绽,function(require, exports, module)
- require 是一個方法,接受 模塊標(biāo)識 作為唯一參數(shù)妆绞,用來獲取其他模塊提供的接口:require(id)
- exports 是一個對象顺呕,用來向外提供模塊接口
- module 是一個對象,上面存儲了與當(dāng)前模塊相關(guān)聯(lián)的一些屬性和方法
不考慮多了一層函數(shù)外括饶,格式和Node.js是一樣的:使用require獲取依賴模塊株茶,使用exports導(dǎo)出API。
// 定義模塊 myModule.js
define(function(require, exports, module) {
var a = require('a'),
b = require('b');
exports.action = function() {};
} );
// 加載模塊
seajs.use(['myModule.js'], function(my){
...
});
有人說CMD是同步的图焰,其實CMD是異步加載的启盛,CMD加載完某個依賴模塊后并不執(zhí)行,只是下載而已楞泼,在所有依賴模塊加載完成后進入主邏輯驰徊,遇到require語句的時候才執(zhí)行對應(yīng)的模塊,這樣模塊的執(zhí)行順序和書寫順序是完全一致的
CMD 與 AMD 區(qū)別
最明顯的區(qū)別就是在模塊定義時對依賴的處理不同
對于依賴的模塊AMD是提前執(zhí)行堕阔,CMD是延遲執(zhí)行棍厂。不過RequireJS從2.0開始,也改成可以延遲執(zhí)行(根據(jù)寫法不同超陆,處理方式不通過)牺弹。
CMD推崇依賴就近,只有在用到某個模塊的時候再去require ,AMD推崇依賴前置时呀。
//AMD
define(['./a','./b'], function (a, b) {
//依賴一開始就寫好
a.test();
b.test();
});
//CMD
define(function (requie, exports, module) {
//依賴可以就近書寫
var a = require('./a');
a.test();
...
//軟依賴
if (status) {
var b = requie('./b');
b.test();
}
});
這種區(qū)別各有優(yōu)劣张漂,只是語法上的差距,而且requireJS和SeaJS都支持對方的寫法
AMD和CMD最大的區(qū)別是對依賴模塊的執(zhí)行時機處理不同谨娜,注意不是加載的時機或者方式不同
同樣都是異步加載模塊航攒,AMD在加載模塊完成后就會執(zhí)行改模塊,所有模塊都加載執(zhí)行完后會進入require的回調(diào)函數(shù)趴梢,執(zhí)行主邏輯漠畜,這樣的效果就是依賴模塊的執(zhí)行順序和書寫順序不一定一致币他,看網(wǎng)絡(luò)速度,哪個先下載下來憔狞,哪個先執(zhí)行蝴悉,但是主邏輯一定在所有依賴加載完成后才執(zhí)行
CMD加載完某個依賴模塊后并不執(zhí)行,只是下載而已瘾敢,在所有依賴模塊加載完成后進入主邏輯拍冠,遇到require語句的時候才執(zhí)行對應(yīng)的模塊,這樣模塊的執(zhí)行順序和書寫順序是完全一致的
這也是很多人說AMD用戶體驗好簇抵,因為沒有延遲庆杜,依賴模塊提前執(zhí)行了,CMD性能好正压,因為只有用戶需要的時候才執(zhí)行的原因
原生js 標(biāo)簽的方式加載
沒什么多說的欣福,其實就是掛載到 window 下
編寫通用的格式
(function (window, factory) {
if (typeof exports === 'object') {
module.exports = factory();
} else if (typeof define === 'function' && define.amd) {
define(factory);
} else {
window.eventUtil = factory();
}
})(this, function () {
// module ...
});
參考jquery,echarts可以寫的更簡單一些
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(factory((global.D3MakeSvgPicClass = {})));
}(this, (function (exports) {
//// module ...
exports.D3MakeSvgPicClass = MakeSvgPicClass;
})));
依賴jquery的庫的通用寫法案例
; (function (factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['jquery'], factory);
} else if (typeof module === 'object' && module.exports) {
// Node/CommonJS
module.exports = function (root, jQuery) {
if (jQuery === undefined) {
// require('jQuery') returns a factory that requires window to
// build a jQuery instance, we normalize how we use modules
// that require this pattern but the window provided is a noop
// if it's defined (how jquery works)
if (typeof window !== 'undefined') {
jQuery = require('jquery');
}
else {
jQuery = require('jquery')(root);
}
}
factory(jQuery);
return jQuery;
};
} else {
// Browser globals
factory(jQuery);
}
}(function (jQuery) {
// 庫的內(nèi)容主體
}));
參考資料:http://www.reibang.com/p/bd4585b737d7
參考資料:https://www.cnblogs.com/zzsdream/p/5158968.html