前端模塊化的規(guī)范

前言

現(xiàn)在學(xué)習(xí)到webpack锦亦,然后重新回過頭來回顧前端模塊化一路發(fā)展過程定義的規(guī)范,遂寫下這篇文令境。

一杠园、模塊化的價值

適用:功能繁多的單頁面(富應(yīng)用頁面)
  多人合作的時候,如果都寫成js文件script標(biāo)簽引入舔庶,那么就要關(guān)心js的順序抛蚁,避免被覆蓋,以及關(guān)心變量惕橙,會不會沖突瞧甩。都是全局變量,很容易沖突吕漂。這在代碼量大亲配,多人協(xié)作的情況下,很災(zāi)難。
  同時吼虎,多人協(xié)作的大項(xiàng)目上犬钢,js文件很多,彼此之間可能會有依賴關(guān)系思灰,比如某個js需要先加載jquery之后才生效玷犹,這樣的。js文件一多洒疚,順序擺放就很重要歹颓,不然依賴關(guān)系就會亂掉,代碼可能就不會發(fā)生作用油湖。這樣可能需要做js文件的整理記錄以及位置順序記錄巍扛,這樣很麻煩,對新人也不友好乏德。浪費(fèi)不必要精力撤奸。
  富應(yīng)用的頁面越來越多,很多頁面邏輯遷移到客戶端實(shí)現(xiàn)喊括,前端代碼越來越多胧瓜。單薄的javascript,無法支撐代碼組織的燃眉之急郑什。

那么前端開發(fā)者們就開始想著使用JavaScript來模擬其他后臺開發(fā)語言(例如:Java)的代碼組織方式府喳,例如package(包):將邏輯相關(guān)的代碼放到同一個包中,每個包之間互不影響蘑拯,即使包內(nèi)變量命名相同也不沖突钝满。使用包時,就import加載使用申窘。
  在JavaScript中要實(shí)現(xiàn)類似的功能舱沧,最先想到的是函數(shù)。將所有函數(shù)抽離出來放置到一個js文件中偶洋,然后引用js文件位置,調(diào)用函數(shù)距糖。但這個方式玄窝,還是無法解決命名沖突的問題,函數(shù)的命名還是要避免沖突悍引。這種純函數(shù)的方式恩脂,需要排除。
那么就來看對象的方式趣斤。
  對象封裝寫法一:

<script>
    /*封裝為一個對象*/
    var block_mock={
          a:1,
          b:2,
          fn1:function(){
              //dosomething
          }
          fn2:function(){
             //dosomething
          }
    }
    /*調(diào)用的寫法*/
    block_mock.fn1();
    block_mock.fn2();
    /*存在問題:對象內(nèi)的變量可以被隨意更改*/
    block_mock.a=100;
</script>

對于寫法一存在的問題俩块,我們來看寫法二:

/*改進(jìn)寫法:寫成一個立即執(zhí)行函數(shù),開始具備模塊化的感覺*/
var block_mock=(function(){
      var a=1;/*局部變量,函數(shù)fn1,fn2可以使用玉凯,避免全局污染*/
      var b=2;
      function fn1(){
          //dosomething
      };
      function fn2(){
          //dosomething
      };
      return{
          fn1:fn1,
          fn2:fn2
      };
})()
/*調(diào)用的寫法*/
block_mock.fn1;
block_mock.fn2;

寫法二中势腮,return是最重要一步,return出來,等于只暴露這一塊漫仆,在模塊外部無法修改我們沒有暴露的變量和函數(shù)捎拯。
  使用對象封裝的寫法,我們可以看到JavaScript具備模塊化的基礎(chǔ)盲厌,來達(dá)到隔離和組裝復(fù)雜代碼的作用署照。繼而,后續(xù)就發(fā)展出完善的前端模塊化的規(guī)范吗浩。這些發(fā)展接著往下講建芙。

往下講之前,我們先結(jié)合前面講的這一堆懂扼,總結(jié)下模塊化的實(shí)現(xiàn)禁荸,帶給我們的預(yù)期好處:

  • 解決命名沖突問題
  • 解決繁瑣的文件依賴問題,實(shí)現(xiàn)管理
  • 代碼能分塊出來微王,可讀性提高了屡限。想修改代碼,也不用從頭到尾找炕倘,只需要找到對應(yīng)的模塊代碼修改就行
  • 提高代碼復(fù)用性:比如曝光組件抽象出來實(shí)現(xiàn)ajax+懶加載+無限加載功能钧大。

二、JavaScript的模塊規(guī)范的發(fā)展

1罩旋、CommonJS

服務(wù)器端的JavaScript——NodeJS在服務(wù)端實(shí)現(xiàn)第一個模塊化的規(guī)范:CommonJS啊央。
  具體用法是:
  1、模塊定義:根據(jù)CommonJS規(guī)范涨醋,一個單獨(dú)的js文件就是一個模塊瓜饥。一個模塊是一個單獨(dú)的作用域,模塊內(nèi)部定義的變量浴骂,外部模塊無法訪問乓土。
  2、模塊輸出:既然外部模塊無法訪問溯警,就需要我模塊本身主動輸出趣苏,這個模塊才是有意義的。按照規(guī)范使用module.exports對象梯轻。
  3食磕、模塊訪問:想要加載某個模塊時,使用require方法喳挑,該方法讀取一個文件并執(zhí)行彬伦,返回文件內(nèi)部的module.exports對象滔悉。

看下面例子:
  注意:下面例子是js文件,在node端運(yùn)行node+文件名单绑,執(zhí)行出結(jié)果回官。
  定義模塊a,并輸出:

var people={
    name:'hyh',
    sayName:function(){
        console.log(this.name);
    }
}
/*最重要,輸出模塊*/
module.exports=people;

定義模塊c,并輸出:

var rabot={
    name:'I am a ranbot',
    walking:function(){
        console.log('I can walking');
    }
}
module.exports=rabot;

注意:測試在a.js里寫多一個rabot對象時询张,前面寫的people對象孙乖,在執(zhí)行module.exports=people時會被后面寫的對象module.exports=rabot覆蓋而無效,所以只能是一個js文件定義一個對象份氧,作為一個模塊唯袄。
  定義模塊b,并在模塊b中加載模塊a和b,使用模塊a和b的方法:

var p=require('./a');
var r=require('./c');
console.log(p);
p.sayName();
r.walking();
console.log('hahahahahaha');

在git后臺中執(zhí)行js文件b.js蜗帜,得到執(zhí)行結(jié)果:

Paste_Image.png

注意:見代碼可知調(diào)用的js——b.js恋拷,是可以同時調(diào)用多個js來使用。

CommonJS是在NodeJS服務(wù)端運(yùn)行使用的規(guī)范厅缺。使用CommonJS規(guī)范的js文件調(diào)用蔬顾,是一個同步的過程,b.js執(zhí)行模塊內(nèi)容湘捎,是需要在本地讀取同個文件夾下的a.js文件和c.js文件诀豁,然后進(jìn)行操作。
  CommonJS的這套過程暴露了缺點(diǎn)窥妇,不適合在瀏覽器端執(zhí)行:
  1舷胜、實(shí)現(xiàn)模式是同步的,需要將所有require都下載下來活翩,然后才執(zhí)行p.sayName()這樣具體的函數(shù)語句烹骨。如果在瀏覽器端做同步實(shí)現(xiàn),那用戶需要等到全部的js文件都下載完成之后材泄,才可以執(zhí)行操作沮焕。這樣的長時間等待,顯然讓用戶抓狂拉宗。而且瀏覽器端的script標(biāo)簽天生是異步的峦树,所有的js文件下載時無法保證按照定義的依賴順序來下載,那么就未免會產(chǎn)生錯誤旦事,導(dǎo)致頁面無效空入。
  2、在瀏覽器端族檬,路徑很難定義,不再是像服務(wù)器一樣化戳,從本地讀取require('./a')单料,這樣的路徑寫法埋凯。

所以,雖然CommonJS是第一個出現(xiàn)的JavaScript模塊化規(guī)范扫尖,但卻是不適用于瀏覽端使用的白对。所以還是看往后發(fā)展的AMD規(guī)范:

2、AMD規(guī)范

AMD 即Asynchronous Module Definition换怖,中文名是異步模塊定義甩恼。ADM規(guī)范其實(shí)是使用requireJS框架的模塊化寫法時要求的規(guī)范,是在瀏覽器端實(shí)現(xiàn)模塊化開發(fā)的規(guī)范沉颂。
  具體用法有:
1条摸、定義模塊:
  define(id?[dependencies]?factory),使用定義的define函數(shù)來定義模塊铸屉。
  id和dependencies為可填項(xiàng)钉蒲,如果不設(shè)定id,加載這個模塊時彻坛,就默認(rèn)使用這個模塊的文件命名顷啼,否則使用id。dependencies為當(dāng)前這個模塊會使用到的依賴昌屉。factory是必填項(xiàng)钙蒙,為模塊的主體內(nèi)容〖渫裕可以是函數(shù)躬厌,也可以是對象。如果是函數(shù)蜻牢,只會被執(zhí)行一次烤咧。如果是對象,則是這個模塊的輸出值抢呆。
2煮嫌、加載模塊:
  require([dependencies],function(){})
  require()函數(shù)接收兩個參數(shù):第一個參數(shù)是一個數(shù)組,表示所依賴的模塊抱虐。第二個參數(shù)是一個回調(diào)函數(shù)昌阿,當(dāng)前面指定的模塊都加載成功后,它將被調(diào)用恳邀。加載的模塊會以參數(shù)形式傳入該函數(shù)懦冰,從而在回調(diào)函數(shù)內(nèi)部就可以使用這些模塊。

** 重點(diǎn):require()函數(shù)在加載依賴的函數(shù)的時候是異步加載的谣沸,這樣瀏覽器不會失去響應(yīng)刷钢,它指定的回調(diào)函數(shù),只有前面的模塊都加載成功后乳附,才會運(yùn)行内地,解決了依賴性的問題伴澄。**
  也就是requireJS實(shí)現(xiàn)滿足瀏覽器的異步要求,避免了因?yàn)榧虞djs文件而頁面停止渲染阱缓,加載文件越多非凌,頁面失去響應(yīng)時間越長的問題了。同時使用require.JS荆针,我們開始不用操心因?yàn)閖s文件間的依賴關(guān)系而需要關(guān)注的文件調(diào)用順序了敞嗡。因?yàn)樗行枰囊蕾囋跀?shù)組中指定后加載,只有加載成功了航背,回調(diào)函數(shù)才會執(zhí)行喉悴。

使用例子:

define('sayname',[],function(){
    var name = 'Byron';
    function sayName(){
        console.log(name);
    }
    return {
        sayName: sayName
    };
}());
require(['jquery','sayname'], function($,my){
  my.sayName(); 
});

$和my參數(shù)就是jquery執(zhí)行后,以及sayname執(zhí)行后返回的return的內(nèi)容沃粗。需要在requireJS框架下執(zhí)行粥惧,因?yàn)闉g覽器不支持require函數(shù)。
  但AMD這樣的規(guī)范寫法最盅,需要我一次把所有依賴寫在數(shù)組里突雪,當(dāng)依賴特別多時,就麻煩凸顯涡贱。所以我們就再追求實(shí)現(xiàn)咏删,在仍然支持依賴異步加載的基礎(chǔ)上,對依賴能夠按需加載。順著這個思路,就講到CMD規(guī)范:

3沽瞭、CMD規(guī)范

相對于AMD,最重要的就是實(shí)現(xiàn)按需加載js文件依賴
  CMD是基于Sea.js的框架的實(shí)現(xiàn)的要求寫法辰狡,現(xiàn)在Sea.js已經(jīng)廢棄不用了,僅作了解垄分。
  看例子:

// 定義模塊  myModule.js
define(function(require, exports, module) {
  var $ = require('jquery.js')
  $('div').addClass('active');
  var timeout=require('...')  /*依賴文件所在路徑*/
  timeout.init()
});

seajs.use(['myModule.js'], function(my){
  //do something
});

看栗子可知宛篇,CMD是需要用到什么,才require什么薄湿,屬于懶執(zhí)行叫倍。AMD對待依賴的態(tài)度是預(yù)執(zhí)行。

結(jié)語

作為前端模塊化豺瘤,組件化的基礎(chǔ)吆倦,所以需要回看一路發(fā)展的規(guī)范。
  發(fā)展到現(xiàn)在坐求,AMD和CMD已經(jīng)實(shí)現(xiàn)相互支持了蚕泽。在requireJS中也可以使用CMD規(guī)范的寫法,實(shí)現(xiàn)相同的效果:例如回到頂部功能的實(shí)現(xiàn)

現(xiàn)在requireJS也是不怎么用了桥嗤,發(fā)展為主流的webpack也是支持CMD和AMD規(guī)范的赛糟。

var React = require('react');

var MyComponent = React.createClass({
    // do something
});

module.exports = MyComponent;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末派任,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子璧南,更是在濱河造成了極大的恐慌,老刑警劉巖师逸,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件司倚,死亡現(xiàn)場離奇詭異,居然都是意外死亡篓像,警方通過查閱死者的電腦和手機(jī)动知,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來员辩,“玉大人盒粮,你說我怎么就攤上這事〉旎” “怎么了丹皱?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長宋税。 經(jīng)常有香客問我摊崭,道長,這世上最難降的妖魔是什么杰赛? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任呢簸,我火速辦了婚禮,結(jié)果婚禮上乏屯,老公的妹妹穿的比我還像新娘根时。我一直安慰自己,他們只是感情好辰晕,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布蛤迎。 她就那樣靜靜地躺著,像睡著了一般伞芹。 火紅的嫁衣襯著肌膚如雪忘苛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天唱较,我揣著相機(jī)與錄音扎唾,去河邊找鬼。 笑死南缓,一個胖子當(dāng)著我的面吹牛胸遇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播汉形,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼纸镊,長吁一口氣:“原來是場噩夢啊……” “哼倍阐!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起逗威,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤峰搪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后凯旭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體概耻,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年罐呼,在試婚紗的時候發(fā)現(xiàn)自己被綠了鞠柄。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡嫉柴,死狀恐怖厌杜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情计螺,我是刑警寧澤夯尽,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站危尿,受9級特大地震影響呐萌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜谊娇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一肺孤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧济欢,春花似錦赠堵、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至半等,卻和暖如春揍愁,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背杀饵。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工莽囤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人切距。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓朽缎,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子话肖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評論 2 354

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