modularization

背景:

  • Javascript 發(fā)展初期就是為了實(shí)現(xiàn)簡(jiǎn)單的頁(yè)面交互邏輯
  • 現(xiàn)在cpu欣福,瀏覽器性能得到極大的提升俄精,很多頁(yè)面邏輯移動(dòng)到了客戶端(表單驗(yàn)證)
  • web2.0 時(shí)代的到來(lái)效床,Ajax 技術(shù)得到廣泛應(yīng)用棺聊,jQuery 等前端庫(kù)層出不窮耀销,前端代碼日益膨脹

導(dǎo)致:

  • JavaScript 作為嵌入式的腳本語(yǔ)言的定位動(dòng)搖
  • JavaScript 沒(méi)有為組織代碼提供明顯幫助
  • 甚至沒(méi)有類的概念自脯,更沒(méi)有模塊(module)的概念了
  • JavaScript 及其簡(jiǎn)單的代碼組織規(guī)范不足以駕馭如此龐大規(guī)模代碼

模塊


  • 這里我們借鑒一下 Java 語(yǔ)言時(shí)怎么處理大規(guī)模程序設(shè)計(jì)的
import java.util.ArrayList;
  • 遺憾的是 JavaScript 在設(shè)計(jì)定位原因之景,沒(méi)有提供類似的功能,開發(fā)者需要模擬出類似的功能膏潮,來(lái)隔離锻狗、組織復(fù)雜的 JavaScript 代碼,我們稱為模塊化
  • 一個(gè)模塊就是實(shí)現(xiàn)特定功能的文件焕参,有了模塊轻纪,我們就可以更方便的使用別人的代碼,想要什么功能叠纷,就加載什么模塊刻帚。模塊開發(fā)需要遵循一定的規(guī)范
  • 規(guī)范形成的過(guò)程是痛苦的,前端的先驅(qū)在刀耕火種涩嚣、茹毛飲血的階段開始崇众,發(fā)展到現(xiàn)在初具規(guī)模掂僵,簡(jiǎn)單了解一下這段不凡的歷程

函數(shù)

  • 函數(shù)一個(gè)功能就是實(shí)現(xiàn)特定邏輯的一組語(yǔ)句打包
  • JavaScript 的作用域就是基于函數(shù)的
  • 所以把函數(shù)作為模塊化的第一步是很自然的事情
  • 在一個(gè)文件里面編寫幾個(gè)相關(guān)函數(shù)就是最開始的模塊了
function fn1(){
  statement
}
function fn2(){
  statement
}

這樣在需要的以后加載函數(shù)所在文件,調(diào)用函數(shù)就可以了

缺點(diǎn):污染了全局變量顷歌,無(wú)法保證不與其他模塊發(fā)生變量名沖突锰蓬,而且模塊成員之間沒(méi)有什么關(guān)系

對(duì)象

為了解決上面問(wèn)題,對(duì)象的寫法因應(yīng)而生眯漩,可以把所有的模塊成員封裝在一個(gè)對(duì)象中

var myModule = {
  var a = 1,
  var b = 2,

  fn1: function(){
    
  },
  fn2: function(){

  }
}

//調(diào)用
myModule.fn2();

這樣避免了變量污染芹扭,只要保證模塊名唯一即可,同時(shí)同一模塊內(nèi)的成員也有了一定關(guān)系

缺點(diǎn):外部可以隨意修改內(nèi)部成員坤塞,會(huì)產(chǎn)生意外的安全問(wèn)題

myModule.a = 100 

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

可以通過(guò)立即執(zhí)行函數(shù)冯勉,來(lái)達(dá)到隱藏細(xì)節(jié)的目的

var myModule = (function(){
  var a = 1
  var b = 2

  function fn1(){

  }
  function fn2(){

  }

  return {
    fn1: fn1,
    fn2: fn2
  }
})()

這樣在模塊外部就無(wú)法修改我們沒(méi)有暴露出來(lái)的變量名、函數(shù)

上述做法就是我們模塊化的基礎(chǔ)摹芙,目前灼狰,通行的 JavaScript 模塊規(guī)范主要有兩種:CommonJsAMD

CommonJs


  • 在網(wǎng)頁(yè)端沒(méi)有模塊化編程只是頁(yè)面JavaScript邏輯復(fù)雜,但也可以工作下去
  • 在服務(wù)器端卻一定要有模塊
  • 所以雖然JavaScriptweb端發(fā)展這么多年浮禾,第一個(gè)流行的模塊化規(guī)范卻由服務(wù)器端的JavaScript應(yīng)用帶來(lái)
  • CommonJS規(guī)范 是由NodeJS發(fā)揚(yáng)光大交胚,這標(biāo)志著JavaScript模塊化編程正式登上舞臺(tái)

簡(jiǎn)單說(shuō)明:

  1. 定義模塊:根據(jù)CommonJs規(guī)范,一個(gè)單獨(dú)的文件就是一個(gè)模塊盈电。每一個(gè)模塊都是一個(gè)單獨(dú)的作用域蝴簇,也就是說(shuō),在該模塊內(nèi)部定義的變量匆帚,無(wú)法被其他模塊讀取熬词,除非定義為global對(duì)象屬性
  2. 模塊輸出:模塊只有要給出口,module.exports對(duì)象吸重,我們需要把模塊希望輸出的內(nèi)容放入該對(duì)象
  3. 加載模塊:加載模塊使用require方法互拾,該方法讀取一個(gè)文件并執(zhí)行,返回文件內(nèi)部的module.exports對(duì)象
//模塊定義 myModule.js
var name = 'liu'

function printName(){
  console.log(name)
}
function printFullName(firstName){
  console.log(firstName + name)
}

module.exports = {
  printName: printName,
  printFullName: printFullName
}

//加載模塊
var nameModule = require('./myModule.js')
nameModule.printName()

不同的實(shí)現(xiàn)對(duì)require時(shí)的路徑有不同要求嚎幸,一般情況可以省略js拓展名颜矿,可以使用相對(duì)路徑,也可以使用絕對(duì)路徑嫉晶,甚至可以省略路徑直接使用模塊名(前提是該模塊時(shí)系統(tǒng)內(nèi)置模塊)

尷尬的瀏覽器


  • 上面的代碼require是同步的骑疆。模塊系統(tǒng)需要同步讀取模塊文件內(nèi)容,并編譯執(zhí)行以得到模塊接口
  • 這在服務(wù)器端實(shí)現(xiàn)和簡(jiǎn)單替废,也很自然箍铭,然而,想在瀏覽器端實(shí)現(xiàn)問(wèn)題卻很多

問(wèn)題:瀏覽器端椎镣,加載JavaScript最佳诈火、最容易的方式是在document中插入script標(biāo)簽。但腳本標(biāo)簽天生異步衣陶,傳統(tǒng)的CommonJS模塊在瀏覽器環(huán)境中無(wú)法正常使用加載

解決:

  • 思路之一是柄瑰,開發(fā)一個(gè)服務(wù)器端組件闸氮,對(duì)模塊代碼作靜態(tài)分析,將模塊與它的依賴列表一起返回給瀏覽器端教沾。這很好使蒲跨,但需要服務(wù)器安裝額外的組件,并因此要調(diào)整一系列底層架構(gòu)
  • 另一種解決思路是授翻,用一套標(biāo)準(zhǔn)模板來(lái)分裝模塊定義或悲,但是對(duì)模塊應(yīng)該怎么定義和怎么加載,又產(chǎn)生了分歧

AMD


  • AMD (Asynchornous Module Dfinition)堪唐,中文名是異步模塊定義的意思巡语。它是一個(gè)在瀏覽器模塊開發(fā)的規(guī)范
  • 由于不是JavaScript原生支持,使用 AMD 規(guī)范進(jìn)行頁(yè)面開發(fā)需要用到對(duì)應(yīng)的庫(kù)函數(shù)淮菠,也就是大名鼎鼎的ReuireJS男公,實(shí)際上 AMD 是RequireJS在推廣過(guò)程中對(duì)模塊定義的規(guī)范化的產(chǎn)出

requireJS主要解決兩個(gè)問(wèn)題:

  1. 多個(gè)js文件可能又依賴關(guān)系,被依賴的文件需要早于依賴它的文件加載到瀏覽器
  2. js加載的時(shí)候?yàn)g覽器會(huì)停止頁(yè)面渲染合陵,加載文件越多枢赔,頁(yè)面失去響應(yīng)時(shí)間越長(zhǎng)

看一個(gè)使用requireJS的例子

//定義模塊 myModule.js
define(['dependency'], function(){
  var name = 'liu'
  function printName(){
    console.log(name)
  }

  return {
    printName: printName
  }
})

//加載模塊
require(['module'], function(my){
  my.printName()
})

語(yǔ)法

requireJS定義了一個(gè)函數(shù)define,它是全局變量拥知,用來(lái)定義模塊

define(id?, dependency?, factory)
  1. id:可選參數(shù)踏拜,用來(lái)定義模塊的標(biāo)識(shí),如果沒(méi)有提供該參數(shù)低剔,腳本文件名(去掉拓展名)
  2. dependencies:是一個(gè)當(dāng)前模塊依賴的模塊名稱數(shù)組
  3. factory:工廠方法速梗,模塊初始化要執(zhí)行的函數(shù)或?qū)ο蟆H绻麨楹瘮?shù)襟齿,它應(yīng)該只被執(zhí)行一次姻锁。如果是對(duì)象,此對(duì)象應(yīng)該為模塊的輸出值

在頁(yè)面上使用require函數(shù)加載模塊

require([dependencies], funciton(){

})

require()函數(shù)接收兩個(gè)參數(shù)

  1. 第一個(gè)參數(shù)是一個(gè)數(shù)組蕊唐,表示所依賴的模塊
  2. 第二個(gè)參數(shù)是一個(gè)回調(diào)函數(shù)屋摔,當(dāng)前頁(yè)面指定的模塊都加載成功后烁设,它將被調(diào)用替梨。加載的模塊會(huì)以參數(shù)形式傳入該函數(shù),從而在回調(diào)函數(shù)內(nèi)部就可以使用這些模塊

require()函數(shù)在加載依賴的函數(shù)的時(shí)候是異步加載的装黑,這樣瀏覽器不會(huì)失去響應(yīng)副瀑,它執(zhí)行的回調(diào)函數(shù),只有前面的模塊都加載成功后恋谭,才會(huì)運(yùn)行糠睡,解決了依賴性的問(wèn)題

CMD


CMD (Common Module Definition) 通用模塊定義, CMD 規(guī)范是國(guó)內(nèi)發(fā)展出來(lái)的疚颊,就像是 AMD 有個(gè)requireJS狈孔,CMD 有個(gè)瀏覽器的實(shí)現(xiàn)seaJS信认,SeaJS要解決的問(wèn)題和requireJS一樣,只不過(guò)在模塊定義方式和模塊加載(可以說(shuō)運(yùn)行均抽、解析)時(shí)機(jī)上有所不同

語(yǔ)法

SeaJS 推薦一個(gè)模塊一個(gè)文件嫁赏,遵循統(tǒng)一的寫法

define

define(id?, deps?, factory)

因?yàn)?CMD 推薦

  1. 一個(gè)文件一個(gè)模塊,所以經(jīng)常就用文件名作為模塊id
  2. CMD 推薦依賴就近油挥,所以一般不再define的參數(shù)中寫依賴潦蝇,在factory中寫
factory有三個(gè)參數(shù)
function(require, exports, module)

require
requirefactory函數(shù)的第一個(gè)參數(shù)

requrie(id)

require是一個(gè)方法,接收模塊標(biāo)識(shí)作為唯一參數(shù)深寥,用來(lái)獲取其他模塊提供的接口

exports
exports是一個(gè)對(duì)象攘乒,用來(lái)向外提供模塊接口

module
module是一個(gè)對(duì)象,上面存儲(chǔ)了與當(dāng)前模塊相關(guān)聯(lián)的一些屬性和方法

下面是一個(gè)例子

//定義模塊 myModule.js
define(function(require, exports, module){
  var $ = require('jquery.js')
  $('div').addClass('active')
})

//加載模塊
seajs.use(['myModule.js'], funciton(my){

})

AMD 與 CMD 的區(qū)別


最明顯的區(qū)別就是:在模塊定義時(shí)對(duì)依賴的處理不同

  1. AMD 推薦依賴前置惋鹅,在定義模塊的時(shí)候就要聲明其依賴的模塊
  2. CMD 推薦就近依賴则酝,只有在用到某模塊的時(shí)候再去require

這種區(qū)別各有優(yōu)劣,知識(shí)語(yǔ)法上的差距闰集,而且requireJSSeaJS都支持對(duì)方的寫法

AMD 和 CMD 最大的區(qū)別是:對(duì)依賴模塊的執(zhí)行時(shí)機(jī)處理不同堤魁,注意不是加載的時(shí)機(jī),而是方式不同

  • 同樣都是異步加載模塊返十,AMD 在加載模塊完成后就會(huì)執(zhí)行該模塊妥泉,所有模塊都加載執(zhí)行完后會(huì)進(jìn)入require的回調(diào)函數(shù),執(zhí)行主邏輯洞坑,這樣的效果就是依賴模塊的執(zhí)行順序和書寫順序不一定一致盲链,看網(wǎng)絡(luò)速度,哪個(gè)先下載下來(lái)迟杂,哪個(gè)先執(zhí)行刽沾,但主邏輯一定在所有依賴加載完成后才執(zhí)行
  • CMD 加載完某個(gè)依賴模塊后并不執(zhí)行,知識(shí)下載而已排拷,在所有依賴模塊加載完成后進(jìn)入主邏輯侧漓,遇到require語(yǔ)句的時(shí)候才執(zhí)行對(duì)應(yīng)的模塊,這樣模塊的執(zhí)行順序和書寫順序完全一致了

這也是很多人說(shuō) AMD 用戶體驗(yàn)好监氢,因?yàn)闆](méi)有延遲布蔗,依賴模塊提前執(zhí)行了,CMD 性能好浪腐,因?yàn)橹挥杏脩粜枰臅r(shí)候才執(zhí)行的原因

參考


從 CommonJS 到 Sea.js

Javascript模塊化編程(一):模塊的寫法

Javascript模塊化編程(二):AMD規(guī)范

詳解JavaScript模塊化開發(fā)

require

SeaJS API 快速參考

SeaJS 與 RequireJS 的異同

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末纵揍,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子议街,更是在濱河造成了極大的恐慌泽谨,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,640評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異吧雹,居然都是意外死亡骨杂,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門雄卷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)腊脱,“玉大人,你說(shuō)我怎么就攤上這事龙亲∩掳迹” “怎么了?”我有些...
    開封第一講書人閱讀 165,011評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵鳄炉,是天一觀的道長(zhǎng)杜耙。 經(jīng)常有香客問(wèn)我,道長(zhǎng)拂盯,這世上最難降的妖魔是什么佑女? 我笑而不...
    開封第一講書人閱讀 58,755評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮团驱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘嚎花。我一直安慰自己呀洲,他們只是感情好紊选,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,774評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著道逗,像睡著了一般兵罢。 火紅的嫁衣襯著肌膚如雪滓窍。 梳的紋絲不亂的頭發(fā)上卖词,一...
    開封第一講書人閱讀 51,610評(píng)論 1 305
  • 那天吏夯,我揣著相機(jī)與錄音此蜈,去河邊找鬼。 笑死锦亦,一個(gè)胖子當(dāng)著我的面吹牛舶替,可吹牛的內(nèi)容都是我干的杠园。 我是一名探鬼主播舔庶,決...
    沈念sama閱讀 40,352評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼陈醒,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼瞧甩!你這毒婦竟也來(lái)了钉跷?” 一聲冷哼從身側(cè)響起肚逸,我...
    開封第一講書人閱讀 39,257評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎朦促,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體血当,經(jīng)...
    沈念sama閱讀 45,717評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡禀忆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,894評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了箩退。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,021評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡撤奸,死狀恐怖喊括,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情郑什,我是刑警寧澤,帶...
    沈念sama閱讀 35,735評(píng)論 5 346
  • 正文 年R本政府宣布蘑拯,位于F島的核電站,受9級(jí)特大地震影響申窘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜剃法,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,354評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望收厨。 院中可真熱鬧,春花似錦诵叁、人聲如沸雁竞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)侥锦。三九已至,卻和暖如春捎拯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背署照。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留建芙,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,224評(píng)論 3 371
  • 正文 我出身青樓右蒲,卻偏偏與公主長(zhǎng)得像赶熟,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子映砖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,974評(píng)論 2 355

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

  • 軟文轉(zhuǎn)自 http://www.cnblogs.com/dolphinX/p/4381855.html 在Java...
    瘋狂烤白薯閱讀 166評(píng)論 0 0
  • 在JavaScript發(fā)展初期就是為了實(shí)現(xiàn)簡(jiǎn)單的頁(yè)面交互邏輯邑退,寥寥數(shù)語(yǔ)即可;如今CPU地技、瀏覽器性能得到了極大的提升...
    簡(jiǎn)不簡(jiǎn)單_都好閱讀 220評(píng)論 0 0
  • 前端模塊化 在JavaScript發(fā)展初期就是為了實(shí)現(xiàn)簡(jiǎn)單的頁(yè)面交互邏輯,寥寥數(shù)語(yǔ)即可飒硅;如今CPU砂缩、瀏覽器性能得到...
    景岳閱讀 243評(píng)論 2 4
  • 在JavaScript發(fā)展初期就是為了實(shí)現(xiàn)簡(jiǎn)單的頁(yè)面交互邏輯梯轻,寥寥數(shù)語(yǔ)即可食磕;如今CPU、瀏覽器性能得到了極大的提升...
    Joel_zh閱讀 146評(píng)論 0 0
  • 在JavaScript發(fā)展初期就是為了實(shí)現(xiàn)簡(jiǎn)單的頁(yè)面交互邏輯滔悉,寥寥數(shù)語(yǔ)即可单绑;如今CPU、瀏覽器性能得到了極大的提升...
    legendtx閱讀 118評(píng)論 0 0