1 個人理解;有錯希望大家指出;稍后更新拖拽上傳文件;
2奴烙、commonJS
commonjs的目標(biāo)是制定一個js模塊化的標(biāo)準(zhǔn),它的目標(biāo)制定一個可以同時在客服端和服務(wù)端運行的模塊独榴。這些模塊擁有自己獨立的作用域,也可以向頂層曝露出自己的api也就是module.exports柱彻。在ES6中common被制定為標(biāo)準(zhǔn)何之。但是在ES6還未被瀏覽器完美支持的情況下,commonjs規(guī)范之能在服務(wù)端發(fā)揮它的作用凭舶。比如在nodejs和webpack等中晌块。而我們服務(wù)端的異步加載模塊主要有2種加載方案,CMD和AMD帅霜,這兩種規(guī)范的典型是seajs和rjs(requirejs)匆背。這兩種方案雖然都是加載模塊的解決方案,但是還是有一些的差別身冀。
我們先來了解下commonjs規(guī)范钝尸。
此規(guī)范指出了如何編寫可以在同類模塊系統(tǒng)中所共用的模塊,這類模塊系統(tǒng)可以同時在客戶端和服務(wù)端搂根,以安全的或者不安全的方式已經(jīng)被實現(xiàn)了或者通過語法擴展可以被未來的系統(tǒng)所支持珍促。這些模塊需要提供頂級作用域的私有性,并提供從其他模塊導(dǎo)入單例對象到自身并且可以導(dǎo)出自身API的能力剩愧。含蓄的說猪叙,這個規(guī)范定義了如果一個模塊系統(tǒng)要支持共用模塊,那么它需要提供的最少的功能特性仁卷。
模塊上下文
在一個模塊中穴翩,存在一個自由的變量"require",它是一個函數(shù)锦积。
這個"require"函數(shù)接收一個模塊標(biāo)識符藏否。
"require"返回外部模塊所輸出的API。
如果出現(xiàn)依賴閉環(huán)(dependency cycle)充包,那么外部模塊在被它的傳遞依賴(transitive dependencies)所require的時候可能并沒有執(zhí)行完成副签;在這種情況下,"require"返回的對象必須至少包含此外部模塊在調(diào)用require函數(shù)(會進入當(dāng)前模塊執(zhí)行環(huán)境)之前就已經(jīng)準(zhǔn)備完畢的輸出基矮。
如果請求的模塊不能返回淆储,那么"require"必須拋出一個錯誤。
在一個模塊中家浇,會存在一個名為"exports"的自由變量本砰,它是一個對象,模塊可以在執(zhí)行的時候把自身的API加入到其中钢悲。
模塊必須使用"exports"對象來做為輸出的唯一表示点额。
如果不理解請移步CommonJS Modules/1.0 規(guī)范 & AMD 規(guī)范中文版這里面會有具體的例子舔株。
從模塊的上下文可以可以看出,這只是制定了一種模塊的方式还棱,在模塊內(nèi)部用require函數(shù)去獲取我們所需要的API载慈,用exports拋出當(dāng)前模塊的API。這其實也就是我們node的模塊方式珍手,沒錯办铡,node就是commonjs的實踐者,commonjs只是一種規(guī)范琳要,而nodejs就是實踐這個規(guī)范寡具。同樣的,ES6中稚补,也執(zhí)行了commonjs規(guī)范童叠,但是介于我們?yōu)g覽器還不能完美的支持ES6,所以我們也不能用這種很cool的方式课幕。(注解:當(dāng)然babel是一個完美的工具拯钻,把ES6轉(zhuǎn)成目前支持的es5。但是最終在瀏覽器中執(zhí)行的代碼還是es5撰豺。所以我們直接編寫的ES6代碼粪般,只能通過webpack等打包工具,把模塊之間的依賴通過打包工具來實現(xiàn)污桦。)
既然無法在瀏覽器中使用ES6亩歹,那么我們?nèi)绾驮跒g覽器中去執(zhí)行模塊依賴加載的呢?這里有2中方案去實現(xiàn)瀏覽器下模塊和依賴加載凡橱,CMD和AMD小作,這兩種方案的典型是seajs和requirejs。下面我們先分析AMD
3稼钩、AMD規(guī)范
AMD是為了彌補commonjs規(guī)范在瀏覽器中目前無法支持ES6的一種解決方案顾稀。異步模塊定義規(guī)范(AMD)制定了定義模塊的規(guī)則,這樣模塊和模塊的依賴可以被異步加載坝撑。這和瀏覽器的異步加載模塊的環(huán)境剛好適應(yīng)(瀏覽器同步加載模塊會導(dǎo)致性能静秆、可用性、調(diào)試和跨域訪問等問題)巡李。
這個規(guī)范只定義define函數(shù)抚笔。
define(id?侨拦,dependencies殊橙?,factory);
其中id和dependencies不是必須的膨蛮。
模塊的格式:
模塊名用來唯一標(biāo)識定義中模塊叠纹,它們同樣在依賴數(shù)組中使用。AMD的模塊名規(guī)范是CommonJS模塊名規(guī)范的超集敞葛。引用如下:
模塊名是由一個或多個單詞以正斜杠為分隔符拼接成的字符串
單詞須為駝峰形式誉察,或者".",".."
模塊名不允許文件擴展名的形式制肮,如".js"
模塊名可以為 "相對的" 或 "頂級的"冒窍。如果首字符為"."或".."則為"相對的"模塊名
頂級的模塊名從根命名空間的概念模塊解析
相對的模塊名從 "require" 書寫和調(diào)用的模塊解析
上文引用的CommonJS模塊id屬性常被用于JavaScript模塊递沪。
相對模塊名解析示例:
如果模塊 "a/b/c"請求"../d", 則解析為"a/d"
如果模塊 "a/b/c"請求"./e", 則解析為"a/b/e"
如果AMD的實現(xiàn)支持加載器插件(Loader-Plugins),則"!"符號用于分隔加載器插件模塊名和插件資源名豺鼻。由于插件資源名可以非常自由地命名,大多數(shù)字符都允許在插件資源名使用款慨。
define.amd (Object)用來標(biāo)識有amd模塊加載器的存在
例子:
- 創(chuàng)建一個名為"alpha"的模塊儒飒,
使用了require,exports檩奠,和名為"beta"的模塊: define("alpha", ["require", "exports", "beta"], function (require, exports, beta) { exports.verb = function { return beta.verb; - Or: return require("beta").verb; } }); //一個返回對象的匿名模塊: define(["alpha"], function (alpha) { return { verb: function{ return alpha.verb + 2; } }; }); //一個沒有依賴性的模塊可以直接定義對象: define({ add: function(x, y){ return x + y; } });
- 一個使用了簡單CommonJS轉(zhuǎn)換的模塊定義: define(function (require, exports, module) { var a = require('a'), b = require('b'); exports.action = function {}; });
4桩了、AMD規(guī)范的實踐者requirejs
requirejs是AMD規(guī)范的實踐者,RequireJS 是一個JavaScript模塊加載器埠戳。它非常適合在瀏覽器中使用井誉,但它也可以用在其他腳本環(huán)境, 就像 Rhino and Node. 使用RequireJS加載模塊化腳本將提高代碼的加載速度和質(zhì)量。
在使用requirejs時整胃,可以查看官方文檔
requirejs非常簡單颗圣,我們只需要定義在頁面加載的時候,引入requirejs并且屁使,把mainjs指定在data-main中在岂,在mainjs中引入我們的requirejs.config和我們需要用到的頁面js,requirejs會根據(jù)我們的模塊去加載相應(yīng)的依賴蛮寂,然后執(zhí)行代碼蔽午。
// 頁面引入 // 模塊,這里使用AMD定義模塊的方式酬蹋,例如及老,定義一個模塊module1 define('module1', ['zepto'], function($) { console.log('this is module1') }) //mainjs內(nèi)容 require.config({ baseUrl: 'amdjs/modules', paths: { main: 'amdjs/main' zepto: 'http://zeptojs.com/zepto.min' }, shim: { }, waitSeconds: 15 }); // 你的模塊 requirejs(['module1'],function($) { console.log('load success!') })
config文件里面有許多參數(shù),這里我把常用的解釋下范抓,具體的請查看>requirejs文檔写半。
baseUrl:所有模塊的查找根路徑。
paths :path映射那些不直接放置于baseUrl下的模塊名尉咕。設(shè)置path時起始位置是相對于baseUrl的叠蝇,除非該path設(shè)置以"/"開頭或含有URL協(xié)議(如http:)。
shim: 為那些沒有使用define來聲明依賴關(guān)系、設(shè)置模塊的"瀏覽器全局變量注入"型腳本做依賴和導(dǎo)出配置悔捶。shim配置僅設(shè)置了代碼的依賴關(guān)系铃慷,想要實際加載shim指定的或涉及的模塊,仍然需要一個常規(guī)的require/define調(diào)用蜕该。設(shè)置shim本身不會觸發(fā)代碼的加載犁柜。
deps: 指定要加載的一個依賴數(shù)組。當(dāng)將require設(shè)置為一個config object在加載require.js之前使用時很有用堂淡。一旦require.js被定義馋缅,這些依賴就已加載。使用deps就像調(diào)用require([])绢淀,但它在loader處理配置完畢之后就立即生效萤悴。它并不阻塞其他的require調(diào)用,它僅是指定某些模塊作為config塊的一部分而異步加載的手段而已皆的。
cmd是commonjs另外的一種模塊加載方案覆履,這個規(guī)范本身偏向于commonjs的規(guī)范。他以一個文件就是一個模塊和ES6中標(biāo)準(zhǔn)的commonjs規(guī)范類似费薄。
它定義了以個define(factory)函數(shù)硝全。define接受factory參數(shù),factory可以是一個函數(shù)楞抡,也可以是一個對象或字符串伟众。
如果factory為函數(shù)時,它有三個參數(shù):require召廷,exports凳厢,module。
define.cmd是一個cmd模塊加載器的標(biāo)識
require方法:同步加載模塊
define(function(require, exports) {
// 獲取模塊 a 的接口
var a = require('./a');
// 調(diào)用模塊 a 的方法 a.doSomething;
});
require.async:用來在模塊的內(nèi)部異步加載模塊柱恤,并且完成后執(zhí)行指定回掉数初。
define(function(require, exports, module) { // 異步加載一個模塊,在加載完成時梗顺,執(zhí)行回調(diào) require.async('./b', function(b) { b.doSomething; }); // 異步加載多個模塊泡孩,在加載完成時,執(zhí)行回調(diào) require.async(['./c', './d'], function(c, d) { c.doSomething; d.doSomething; }); });
require.resolve:使用模塊系統(tǒng)內(nèi)部的路徑解析機制來解析并返回模塊路徑寺谤。該函數(shù)不會加載模塊仑鸥,只返回解析后的絕對路徑。
define(function(require, exports) { console.log(require.resolve('./b')); // ==> http://example.com/path/to/b.js });
exports:是一個對象变屁,用來向外提供模塊接口眼俊。
define(function(require) { // 通過 return 直接提供接口 return { foo: 'bar', doSomething: function {} }; });
module.exports:模塊暴露的出口
define(function(require, exports, module) { // 正確寫法 module.exports = { foo: 'bar', doSomething: function {} }; });
6、CMD規(guī)范的實踐者seaJS
seajs和requirejs的加載方式類似粟关,在頁面引入seajs文件后疮胖,加載seajs.config,并且之后加載mainjs。詳細(xì)信息請查看seajs文檔澎灸。
7院塞、CMD和AMD之間的差異
1)AMD(異步加載模塊),CMD(通用模塊)性昭,AMD是需要通過異步加載的形式把依賴加載進來拦止,然而CMD在require依賴的時候,可以通過同步的形式(require)糜颠,也可以通過異步的形式(require.async)汹族。當(dāng)然AMD也可以通過特殊的寫法支持CMD,但是不推崇其兴。
2)CMD 推崇依賴就近顶瞒,AMD 推崇依賴前置。在AMD中忌警,我們需要把依賴前置在依賴數(shù)組中搁拙。而在cmd中秒梳,我們只需要在使用這個模塊前法绵,把依賴的模塊require進來。
3)設(shè)計理念不一樣酪碘,在 SeaJS 里朋譬,API 的設(shè)計理念是:
保持簡單,職責(zé)單一兴垦。
遵守規(guī)范徙赢,但不拘泥。
適度靈活
requirejs中探越,require的用法多樣狡赐,比如:
require('a') -- gets exports of module a
require(['a']) -- fetch module a according to module name scheme
require(['a.js']) -- fetch a.js directly relative to current page
require({...}) -- set loader config
4)聚焦點有差異
seajs專注于瀏覽器環(huán)境下的模塊加載,而requirejs集成了在node環(huán)境以及Rhino 環(huán)境下的代碼钦幔,這導(dǎo)致requirejs比seajs更大枕屉。
參考文獻(xiàn):
注:本人見識短淺,有不夠準(zhǔn)確的地方萬望指正鲤氢。拜謝搀擂!