RequireJs的使用和快速理解

RequireJs已經(jīng)流行很久了废士,它提供了以下功能:

  • 聲明不同js文件之間的依賴
  • 可以按需、并行、延時載入js庫
  • 可以讓我們的代碼以模塊化的方式組織
  • 初看起來并不復雜。

在html中引入requirejs

在HTML中禁悠,添加這樣的 < script> 標簽:

<script src="/path/to/require.js" data-main="/path/to/app/config.js"></script>

通常使用requirejs的話,我們只需要導入requirejs即可汰蓉,不需要顯式導入其它的js庫绷蹲,因為這個工作會交給requirejs來做棒卷。
屬性 data-main 是告訴requirejs:你下載完以后顾孽,馬上去載入真正的入口文件。它一般用來對requirejs進行配置比规,并且載入真正的程序模塊若厚。

在config.js中配置requirejs

config.js 中通常用來做兩件事:
1、配置requirejs 比如項目中用到哪些模塊蜒什,文件路徑是什么
2测秸、載入程序主模塊

requirejs.config({
  baseUrl: '/public/js',
  paths: {
    app: 'app'
  }
});

requirejs(['app'], function(app) {
  app.hello();
});

在 paths 中,我們聲明了一個名為 app 的模塊灾常,以及它對應的js文件地址霎冯。在最理想的情況下, app.js 的內(nèi)容钞瀑,應該使用requirejs的方式來定義模塊:

define([], function() {
  return {
    hello: function() {
      alert("hello, app~");
    }
  }
});

這里的 define 是requirejs提供的函數(shù)沈撞。requirejs一共提供了兩個全局變量:
1、requirejs/require: 用來配置requirejs及載入入口模塊雕什。如果其中一個命名被其它庫使用了缠俺,我們可以用另一個 define: 定義一個模塊
2、另外還可以把 require 當作依賴的模塊贷岸,然后調(diào)用它的方法:

define(["require"], function(require) {
    var cssUrl = require.toUrl("./style.css");
});

依賴一個不使用requirejs方式的庫

前面的代碼是理想的情況壹士,即依賴的js文件,里面用了 define(...) 這樣的方式來組織代碼的偿警。如果沒用這種方式躏救,會出現(xiàn)什么情況?
比如這個 hello.js :

function hello() {
  alert("hello, world~");
}

它就按最普通的方式定義了一個函數(shù)螟蒸,我們能在requirejs里使用它嗎落剪?
先看下面不能正確工作的代碼:

requirejs.config({
  baseUrl: '/public/js',
  paths: {
    hello: 'hello'
  }
});

requirejs(['hello'], function(hello) {
  hello();
});

這段代碼會報錯,提示:

Uncaught TypeError: undefined is not a function 

原因是最后調(diào)用 hello() 的時候尿庐,這個 hello 是個 undefined . 這說明忠怖,雖然我們依賴了一個js庫(它會被載入),但requirejs無法從中拿到代表它的對象注入進來供我們使用抄瑟。

在這種情況下凡泣,我們要使用 shim 枉疼,將某個依賴中的某個全局變量暴露給requirejs,當作這個模塊本身的引用鞋拟。

requirejs.config({
  baseUrl: '/public/js',
  paths: {
    hello: 'hello'
  },
  shim: {
    hello: { exports: 'hello' }
  }
});

requirejs(['hello'], function(hello) {
  hello();
});

再運行就正常了骂维。
上面代碼 exports: 'hello' 中的 hello ,是我們在 hello.js 中定義的 hello 函數(shù)贺纲。當我們使用 function hello() {} 的方式定義一個函數(shù)的時候航闺,它就是全局可用的。如果我們選擇了把它 export 給requirejs猴誊,那當我們的代碼依賴于 hello 模塊的時候潦刃,就可以拿到這個 hello 函數(shù)的引用了。
所以: exports 可以把某個非requirejs方式的代碼中的某一個全局變量暴露出去懈叹,當作該模塊以引用乖杠。

暴露多個變量:init

但如果我要同時暴露多個全局變量呢?比如澄成, hello.js 的定義其實是這樣的:

function hello() {
  alert("hello, world~");
}
function hello2() {
  alert("hello, world, again~");
}

它定義了兩個函數(shù)胧洒,而我兩個都想要。

這時就不能再用 exports 了墨状,必須換成 init 函數(shù):

requirejs.config({
  baseUrl: '/public/js',
  paths: {
    hello: 'hello'
  },
  shim: {
    hello: {
      init: function() {
        return {
          hello: hello,
          hello2: hello2
        }
      }
    }
  }
});

requirejs(['hello'], function(hello) {
  hello.hello1();
  hello.hello2();
});

當 exports 與 init 同時存在的時候卫漫, exports 將被忽略。

無主的與有主的模塊

我遇到了一個折騰我不少時間的問題:為什么我只能使用 jquery 來依賴jquery, 而不能用其它的名字肾砂?
比如下面這段代碼:

requirejs.config({
  baseUrl: '/public/js',
  paths: {
    hello: 'hello'
  },
  shim: {
    hello: {
      init: function() {
        return {
          hello: hello,
          hello2: hello2
        }
      }
    }
  }
});

requirejs(['hello'], function(hello) {
  hello.hello1();
  hello.hello2();
});

它會提示我:

jq is undefined

但我僅僅改個名字:

requirejs.config({
  baseUrl: '/public/js',
  paths: {
    jquery: 'lib/jquery/jquery'
  }
});

requirejs(['jquery'], function(jq) {
  alert(jq);
});

就一切正常了列赎,能打印出 jq 相應的對象了。
為什么通今?我始終沒搞清楚問題在哪兒粥谬。

有主的模塊

經(jīng)常研究,發(fā)現(xiàn)原來在jquery中已經(jīng)定義了:

define('jquery', [], function() { ... });

它這里的 define 跟我們前面看到的 app.js 不同辫塌,在于它多了第一個參數(shù) 'jquery' 漏策,表示給當前這個模塊起了名字 jquery ,它已經(jīng)是有主的了,只能屬于 jquery .
所以當我們使用另一個名字:

myjquery: 'lib/jquery/jquery'

去引用這個庫的時候,它會發(fā)現(xiàn)集乔,在 jquery.js 里聲明的模塊名 jquery 與我自己使用的模塊名 myjquery 不能,便不會把它賦給 myjquery 感耙,所以 myjquery 的值是 undefined 。
所以我們在使用一個第三方的時候持隧,一定要注意它是否聲明了一個確定的模塊名即硼。

無主的模塊

如果我們不指明模塊名,就像這樣:

define([...], function() {
  ...
});

那么它就是無主的模塊屡拨。我們可以在 requirejs.config 里只酥,使用任意一個模塊名來引用它褥实。這樣的話,就讓我們的命名非常自由裂允,大部分的模塊就是無主的损离。

為什么有的有主,有的無主

可以看到绝编,無主的模塊使用起來非常自由僻澎,為什么某些庫(jquery, underscore)要把自己聲明為有主的呢?
按某些說法十饥,這么做是出于性能的考慮窟勃。因為像 jquery , underscore 這樣的基礎庫,經(jīng)常被其它的庫依賴绷跑。如果聲明為無主的拳恋,那么其它的庫很可能起不同的模塊名凡资,這樣當我們使用它們時砸捏,就可能會多次載入jquery/underscore。
而把它們聲明為有主的隙赁,那么所有的模塊只能使用同一個名字引用它們垦藏,這樣系統(tǒng)就只會載入它們一次。

挖墻角

對于有主的模塊伞访,我們還有一種方式可以挖墻角:不把它們當作滿足requirejs規(guī)范的模塊掂骏,而當作普通js庫,然后在 shim 中導出它們定義的全局變量厚掷。

requirejs.config({
  baseUrl: '/public/js',
  paths: {
    myjquery: 'lib/jquery/jquery'
  },
  shim: {
    myjquery: { exports: 'jQuery' }
  }
});

requirejs(['myjquery'], function(jq) {
  alert(jq);
});

這樣通過暴露 jQuery 這個全局變量給 myjquery 弟灼,我們就能正常的使用它了。
不過我們完全沒有必要這么挖墻角冒黑,因為對于我們來說田绑,似乎沒有任何好處。

如何完全不讓jquery污染全局的$

在前面引用jquery的這幾種方式中抡爹,我們雖然可以以模塊的方式拿到jquery模塊的引用掩驱,但是還是可以在任何地方使用全局變量 jQuery 和 $ 。有沒有辦法讓jquery完全不污染這兩個變量冬竟?

在init中調(diào)用noConflict (無效)

首先嘗試一種最簡單但是不工作的方式:

requirejs.config({
  baseUrl: '/public/js',
  paths: {
    jquery: 'lib/jquery/jquery'
  },
  shim: {
    jquery: {
      init: function() {
        return jQuery.noConflict(true);
      }
    }
  }
});

requirejs(['jquery'], function(jq) {
  alert($);
});

這樣是不工作的欧穴,還是會彈出來一個非 undefined 的值。其原因是泵殴,一旦requirejs為模塊名 jquery 找到了屬于它的模塊涮帘,它就會忽略 shim 中相應的內(nèi)容。也就是說笑诅,下面這段代碼完全沒有執(zhí)行:

jquery: {
  init: function() {
    return jQuery.noConflict(true);
  }
}

使用另一個名字

如果我們使用挖墻角的方式來使用jquery调缨,如下:

requirejs.config({
  baseUrl: '/public/js',
  paths: {
    myjquery: 'lib/jquery/jquery'
  },
  shim: {
    myjquery: {
      init: function() {
        return jQuery.noConflict(true);
      }
    }
  }
});

requirejs(['myjquery'], function(jq) {
  alert($);
});

這樣的確有效映屋,這時彈出來的就是一個 undefined 。但是這樣做的問題是同蜻,如果我們引用的某個第三方庫還是使用 jquery 來引用jquery棚点,那么就會報“找不到模塊”的錯了。
我們要么得手動修改第三方模塊的代碼湾蔓,要么再為它們提供一個 jquery 模塊瘫析。但是使用后者的話,全局變量 $ 可能又重新被污染了默责。

使用map

如果我們有辦法能讓在繼續(xù)使用 jquery 這個模塊名的同時贬循,有機會調(diào)用 jQuery.noConflict(true) 就好了。
我們可以再定義一個模塊桃序,僅僅為了執(zhí)行這句代碼:
jquery-private.js

define(['jquery'], function(jq) {
  return jQuery.noConflict(true);
});

然后在入口處先調(diào)用它:

requirejs.config({
  baseUrl: '/public/js',
  paths: {
    jquery: 'lib/jquery/jquery',
    'jquery-private': 'jquery-private'
  }
});

requirejs(['jquery-private', 'jquery'], function() {
  alert($);
});

這樣的確可行杖虾,但是還是會有問題: 我們必須小心的確保 jquery-private 永遠是第一個被依賴,這樣它才有機會盡早調(diào)用 jQuery.noConflict(true) 清除全局變量 $ 和 jQuery 媒熊。這種保證只能靠人奇适,非常不可靠。
我們這時可以引入 map 配置芦鳍,一勞永逸地解決這樣問題:

requirejs.config({
  baseUrl: '/public/js',
  paths: {
    jquery: 'lib/jquery/jquery',
    'jquery-private': 'jquery-private'
  },
  map: {
    '*': { 'jquery': 'jquery-private'},
    'jquery-private': { 'jquery': 'jquery'}
  }
});

requirejs(['jquery'], function(jq) {
  alert($);
});

這樣做嚷往,就解決了前面的問題:在除了jquery-private之外的任何依賴中,還可以直接使用 jqurey 這個模塊名柠衅,并且總是被替換為對 jquery-private 的依賴皮仁,使得它最先被執(zhí)行。

小結(jié)

與requirejs類似的還有seajs等等菲宴。javascript模塊化編程很重要贷祈,requirejs屬于AMD規(guī)范,關于AMD 規(guī)范喝峦,請看:https://github.com/amdjs/amdjs-api/wiki/AMD
假如您也是前端势誊,可以在項目中用一下!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末愈犹,一起剝皮案震驚了整個濱河市键科,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌漩怎,老刑警劉巖勋颖,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異勋锤,居然都是意外死亡饭玲,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進店門叁执,熙熙樓的掌柜王于貴愁眉苦臉地迎上來茄厘,“玉大人矮冬,你說我怎么就攤上這事〈喂” “怎么了胎署?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長窑滞。 經(jīng)常有香客問我琼牧,道長,這世上最難降的妖魔是什么哀卫? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任巨坊,我火速辦了婚禮,結(jié)果婚禮上此改,老公的妹妹穿的比我還像新娘趾撵。我一直安慰自己,他們只是感情好共啃,可當我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布占调。 她就那樣靜靜地躺著,像睡著了一般勋磕。 火紅的嫁衣襯著肌膚如雪妈候。 梳的紋絲不亂的頭發(fā)上敢靡,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天挂滓,我揣著相機與錄音,去河邊找鬼啸胧。 笑死赶站,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的纺念。 我是一名探鬼主播贝椿,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼陷谱!你這毒婦竟也來了烙博?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤烟逊,失蹤者是張志新(化名)和其女友劉穎渣窜,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體宪躯,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡乔宿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了访雪。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片详瑞。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡掂林,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出坝橡,到底是詐尸還是另有隱情泻帮,我是刑警寧澤,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布计寇,位于F島的核電站刑顺,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏饲常。R本人自食惡果不足惜蹲堂,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望贝淤。 院中可真熱鬧柒竞,春花似錦、人聲如沸播聪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽离陶。三九已至稼虎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間招刨,已是汗流浹背霎俩。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留沉眶,地道東北人打却。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像谎倔,于是被迫代替她去往敵國和親柳击。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,077評論 2 355

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

  • 請參看我github中的wiki片习,不定期更新捌肴。https://github.com/ivonzhang/Front...
    zhangivon閱讀 7,131評論 2 19
  • 雖然從沒有認為自己是一個前端開發(fā)者,但不知不覺中也積累下了一些前端開發(fā)的經(jīng)驗藕咏。正巧之前碰到一道面試題状知,于是就順便梳...
    AlloVince閱讀 6,966評論 1 49
  • 在線閱讀 http://interview.poetries.top[http://interview.poetr...
    程序員poetry閱讀 114,400評論 24 450
  • 導語: 之前一直有聽說RequireJS,但是一直都沒機會去了解侈离,只知道它是一個給js做模塊化的API试幽。最近在做R...
    wuqke閱讀 40,921評論 11 78
  • 文/暖先森 每一段凄美動人的童話背后,都有一段言不由衷的謊話。 我家養(yǎng)的那條小白狗铺坞,是銀狐和獅子狗的混血兒起宽。但他卻...
    暖先森閱讀 19,556評論 20 47