前端模塊化(CommonJS,AMD和CMD)

轉(zhuǎn)載于作者:linwalker

via:http://www.reibang.com/p/d67bc79976e6

前端模塊規(guī)范有三種:CommonJs,AMD和CMD。

CommonJs用在服務(wù)器端,AMD和CMD用在瀏覽器環(huán)境栽渴。

AMD 是 RequireJS 在推廣過程中對模塊定義的規(guī)范化產(chǎn)出茉继。

CMD 是 SeaJS 在推廣過程中對模塊定義的規(guī)范化產(chǎn)出坯临。

AMD:提前執(zhí)行(異步加載:依賴先執(zhí)行)+延遲執(zhí)行

CMD:延遲執(zhí)行(運行到需加載纳本,根據(jù)順序執(zhí)行)

模塊

函數(shù)寫法

function f1(){ //...}function f2(){ //...}

模塊就是實現(xiàn)特定功能的文件苔可,把幾個函數(shù)放在一個文件里就構(gòu)成了一個模塊既绕。需要的時候加載這個文件啄刹,調(diào)用其中的函數(shù)即可。

但這樣做會污染全局變量凄贩,無法保證不與其他模塊發(fā)生變量名沖突誓军,而且模塊成員之間沒什么關(guān)系。

對象寫法

var module = { star : 0, f1 : function (){ //... }, f2 : function (){ //... }};module.f1();module.star = 1;

模塊寫成一個對象疲扎,模塊成員都封裝在對象里昵时,通過調(diào)用對象屬性,訪問使用模塊成員椒丧。但同時也暴露了模塊成員壹甥,外部可以修改模塊內(nèi)部狀態(tài)。

立即執(zhí)行函數(shù)

var module = (function(){ var star = 0; var f1 = function (){ console.log('ok'); }; var f2 = function (){ //... }; return { f1:f1, f2:f2 }; })();module.f1(); //okconsole.log(module.star) //undefined

外部無法訪問內(nèi)部私有變量

CommonJs

CommonJS是服務(wù)器端模塊的規(guī)范壶熏,由Node推廣使用句柠。由于服務(wù)端編程的復(fù)雜性,如果沒有模塊很難與操作系統(tǒng)及其他應(yīng)用程序互動。使用方法如下:

math.jsexports.add = function() { var sum = 0, i = 0, args = arguments, l = args.length; while (i < l) { sum += args[i++]; } return sum;};increment.jsvar add = require('math').add;exports.increment = function(val) { return add(val, 1);};index.jsvar increment = require('increment').increment;var a = increment(1); //2

根據(jù)CommonJS規(guī)范:

一個單獨的文件就是一個模塊溯职。每一個模塊都是一個單獨的作用域精盅,也就是說,在該模塊內(nèi)部定義的變量谜酒,無法被其他模塊讀取叹俏,除非定義為global對象的屬性。

輸出模塊變量的最好方法是使用module.exports對象僻族。

加載模塊使用require方法粘驰,該方法讀取一個文件并執(zhí)行,返回文件內(nèi)部的module.exports對象

仔細(xì)看上面的代碼述么,您會注意到 require 是同步的蝌数。模塊系統(tǒng)需要同步讀取模塊文件內(nèi)容,并編譯執(zhí)行以得到模塊接口碉输。

然而籽前, 這在瀏覽器端問題多多。

瀏覽器端敷钾,加載 Java 最佳枝哄、最容易的方式是在 document 中插入<>標(biāo)簽。但腳本標(biāo)簽天生異步阻荒,傳統(tǒng) CommonJS 模塊在瀏覽器環(huán)境中無法正常加載挠锥。

解決思路之一是,開發(fā)一個服務(wù)器端組件侨赡,對模塊代碼作靜態(tài)分析蓖租,將模塊與它的依賴列表一起返回給瀏覽器端。 這很好使羊壹,但需要服務(wù)器安裝額外的組件蓖宦,并因此要調(diào)整一系列底層架構(gòu)。

另一種解決思路是油猫,用一套標(biāo)準(zhǔn)模板來封裝模塊定義:

define(function(require, exports, module) { // The module code goes here});

這套模板代碼為模塊加載器提供了機(jī)會稠茂,使其能在模塊代碼執(zhí)行之前,對模塊代碼進(jìn)行靜態(tài)分析情妖,并動態(tài)生成依賴列表睬关。

math.jsdefine(function(require, exports, module) { exports.add = function() { var sum = 0, i = 0, args = arguments, l = args.length; while (i < l) { sum += args[i++]; } return sum; };});increment.jsdefine(function(require, exports, module) { var add = require('math').add; exports.increment = function(val) { return add(val, 1); };});index.jsdefine(function(require, exports, module) { var inc = require('increment').increment; inc(1); // 2});AMD

AMD是"Asynchronous Module Definition"的縮寫,意思就是"異步模塊定義"毡证。由于不是Java原生支持电爹,使用AMD規(guī)范進(jìn)行頁面開發(fā)需要用到對應(yīng)的庫函數(shù),也就是大名鼎鼎RequireJS料睛,實際上AMD 是 RequireJS 在推廣過程中對模塊定義的規(guī)范化的產(chǎn)出

它采用異步方式加載模塊丐箩,模塊的加載不影響它后面語句的運行摇邦。所有依賴這個模塊的語句,都定義在一個回調(diào)函數(shù)中雏蛮,等到加載完成之后涎嚼,這個回調(diào)函數(shù)才會運行阱州。

RequireJS主要解決兩個問題

多個js文件可能有依賴關(guān)系挑秉,被依賴的文件需要早于依賴它的文件加載到瀏覽器

js加載的時候瀏覽器會停止頁面渲染,加載文件越多苔货,頁面失去響應(yīng)時間越長

RequireJs也采用require()語句加載模塊犀概,但是不同于CommonJS,它要求兩個參數(shù):

第一個參數(shù)[module]夜惭,是一個數(shù)組姻灶,里面的成員就是要加載的模塊;第二個參數(shù)callback诈茧,則是加載成功之后的回調(diào)函數(shù)产喉。math.add()與math模塊加載不是同步的,瀏覽器不會發(fā)生假死敢会。

require([module], callback);require([increment'], function (increment) { increment.add(1);});

define()函數(shù)

RequireJS定義了一個函數(shù) define曾沈,它是全局變量,用來定義模塊:

define(id?, dependencies?, factory);

參數(shù)說明:

id:指定義中模塊的名字鸥昏,可選塞俱;如果沒有提供該參數(shù),模塊的名字應(yīng)該默認(rèn)為模塊加載器請求的指定腳本的名字吏垮。如果提供了該參數(shù)障涯,模塊名必須是“頂級”的和絕對的(不允許相對名字)。

依賴dependencies:是一個當(dāng)前模塊依賴的膳汪,已被模塊定義的模塊標(biāo)識的數(shù)組字面量唯蝶。

依賴參數(shù)是可選的,如果忽略此參數(shù)遗嗽,它應(yīng)該默認(rèn)為["require", "exports", "module"]粘我。然而,如果工廠方法的長度屬性小于3媳谁,加載器會選擇以函數(shù)的長度屬性指定的參數(shù)個數(shù)調(diào)用工廠方法涂滴。

工廠方法factory,模塊初始化要執(zhí)行的函數(shù)或?qū)ο笄缫簟H绻麨楹瘮?shù)柔纵,它應(yīng)該只被執(zhí)行一次。如果是對象锤躁,此對象應(yīng)該為模塊的輸出值搁料。

來舉個例子看看:

define("alpha", ["require", "exports", "beta"], function (require, exports, beta) { exports.verb = function() { return beta.verb(); //Or: return require("beta").verb(); } });

RequireJs使用例子

require.config是用來定義別名的,在paths屬性下配置別名。然后通過requirejs(參數(shù)一郭计,參數(shù)二)霸琴;參數(shù)一是數(shù)組,傳入我們需要引用的模塊名昭伸,第二個參數(shù)是個回調(diào)函數(shù)梧乘,回調(diào)函數(shù)傳入一個變量,代替剛才所引入的模塊庐杨。

main.js//別名配置requirejs.config({ paths: { jquery: 'jquery.min' //可以省略.js }});//引入模塊选调,用變量$表示jquery模塊requirejs(['jquery'], function ($) { $('body').css('background-color','red');});

引入模塊也可以只寫require()。requirejs通過define()定義模塊灵份,定義的參數(shù)上同仁堪。在此模塊內(nèi)的方法和變量外部是無法訪問的,只有通過return返回才行.

math.jsdefine('math',['jquery'], function ($) {//引入jQuery模塊 return { add: function(x,y){ return x + y; } };});

將該模塊命名為math.js保存填渠。

require(['jquery','math'], function ($,math) { console.log(math.add(10,100));//110});

main.js引入模塊方法

CMD

CMD 即Common Module Definition通用模塊定義弦聂,CMD規(guī)范是國內(nèi)發(fā)展出來的,就像AMD有個requireJS氛什,CMD有個瀏覽器的實現(xiàn)SeaJS莺葫,SeaJS要解決的問題和requireJS一樣,只不過在模塊定義方式和模塊加載(可以說運行屉更、解析)時機(jī)上有所不同徙融。

在 CMD 規(guī)范中,一個模塊就是一個文件瑰谜。代碼的書寫格式如下:

define(function(require, exports, module) { // 模塊代碼});

require是可以把其他模塊導(dǎo)入進(jìn)來的一個參數(shù);而exports是可以把模塊內(nèi)的一些屬性和方法導(dǎo)出的;module 是一個對象欺冀,上面存儲了與當(dāng)前模塊相關(guān)聯(lián)的一些屬性和方法。

AMD是依賴關(guān)系前置,在定義模塊的時候就要聲明其依賴的模塊;

CMD是按需加載依賴就近,只有在用到某個模塊的時候再去require:

// CMDdefine(function(require, exports, module) { var a = require('./a') a.doSomething() // 此處略去 100 行 var b = require('./b') // 依賴可以就近書寫 b.doSomething() // ... })// AMD 默認(rèn)推薦的是define(['./a', './b'], function(a, b) { // 依賴必須一開始就寫好 a.doSomething() // 此處略去 100 行 b.doSomething() ...})

seajs使用例子

// 定義模塊 myModule.jsdefine(function(require, exports, module) { var $ = require('jquery.js') $('div').addClass('active'); exports.data = 1;});// 加載模塊seajs.use(['myModule.js'], function(my){ var star= my.data; console.log(star); //1});

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末萨脑,一起剝皮案震驚了整個濱河市隐轩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌渤早,老刑警劉巖职车,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異鹊杖,居然都是意外死亡悴灵,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門骂蓖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來积瞒,“玉大人,你說我怎么就攤上這事登下∶?祝” “怎么了叮喳?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長缰贝。 經(jīng)常有香客問我馍悟,道長,這世上最難降的妖魔是什么剩晴? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任锣咒,我火速辦了婚禮,結(jié)果婚禮上李破,老公的妹妹穿的比我還像新娘宠哄。我一直安慰自己,他們只是感情好嗤攻,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著诽俯,像睡著了一般妇菱。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上暴区,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天闯团,我揣著相機(jī)與錄音,去河邊找鬼仙粱。 笑死房交,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的伐割。 我是一名探鬼主播候味,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼隔心!你這毒婦竟也來了白群?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤硬霍,失蹤者是張志新(化名)和其女友劉穎帜慢,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體唯卖,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡粱玲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了拜轨。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片抽减。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖撩轰,靈堂內(nèi)的尸體忽然破棺而出胯甩,到底是詐尸還是另有隱情昧廷,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布偎箫,位于F島的核電站木柬,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏淹办。R本人自食惡果不足惜眉枕,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望怜森。 院中可真熱鬧速挑,春花似錦、人聲如沸副硅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽恐疲。三九已至腊满,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間培己,已是汗流浹背碳蛋。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留省咨,地道東北人肃弟。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像零蓉,于是被迫代替她去往敵國和親笤受。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容