一:爲何要使用requireJS?html
好久以前,咱們全部的JS文件寫到一個js文件裏面去進行加載,可是當業務愈來愈複雜的時候,須要分紅多個JS文件進行加載,好比在頁面中head內分別引入a.js,b.js,c.js等,以下所示: node
<script src="js/app/a.js"></script> <script src="js/app/b.js"></script> <script src="js/app/c.js"></script>
咱們如今先在瀏覽器下看看這些請求,以下所示:jquery
這樣的寫法有以下缺點:數組
1. 頁面在加載的時候,是從頁面自上往下加載及渲染的,當頁面上有多個分散的js文件時候,頁面會先加載及解析頭部的JS文件(同步加載),頁面被堵塞了,其次分散的js請求數多了,網頁失去響應的時間就會變長。瀏覽器
2. 因爲JS文件存在依賴關係,好比上面的b.js要依賴於a.js,因此務必保證a.js優先引入到頁面上來且先加載,要嚴格保證加載順序,依賴性最大的文件必定要放到最後加載。可是當依賴關係很複雜的時候,代碼的編寫和維護就會變得困難了。app
固然上面引入JS時候,對於第1點:首先:咱們能夠放在底部去加載,把全部JS放在</body>以前去,這樣就會解決了遊覽器堵塞的問題,其次咱們能夠把全部的JS文件打包成一個JS文件,可是依賴性(也就是順序)咱們仍是沒有辦法解決掉,因此咱們引入了requireJS。jquery插件
二:使用requireJS的優勢有哪些?異步
1. 實現JS文件的異步加載,避免網頁被堵塞。async
2. 管理模塊之間的依賴性,便於代碼的編寫和維護。函數
requireJS基本語法及使用.
1. 首先咱們須要到官網下載最新版本的requireJS源碼包。 下載地址: ,
在頁面頭部head標籤內引入requireJS,以下:<script src="js/require.js"></script>,可是加載這個文件也會形成網頁失去響應,咱們能夠加上 defer 和 async這個屬性。以下:
<script src="js/require.js" defer async="true" ></script>
Async屬性代表文件須要異步加載,IE不支持這個屬性,只支持defer,因此上面把這2個屬性都加上。接下來,看看requireJS啓動加載腳本的初始化方式,requireJS支持屬性 data-main 這個屬性來加載初始化的JS文件,以下:
<script src="js/require.js" defer async="true" data-main="js/app.js"></script>
上面的意思是:先異步加載requireJS文件,完成後繼續異步加載app.js文件,假如app.js內容爲空的話,咱們能夠看看加載順序以下:
上面的app.js後的.js能夠去掉,由於requireJS源碼已經默認都是之後綴JS文件結尾的。
2. 如何定義模塊文件?
RequireJS編寫模塊不一樣於其餘腳本文件,它良好的使用define來定義一個做用域避免全局空間污染,它能夠顯示出其依賴關係,並以函數(定義此模塊的那個函數)參數的形式將這些依賴進行注入。
下面咱們來看下demo,以下新建一個項目文件:
咱們先在app/b.js下添加基本的requireJS代碼以下:
// b.js define(function(){ var add = function(x,y) { return x + y; }; return { add : add } });
使用define來定義模塊,下面咱們須要在app.js裏面來加載b.js模塊,以下在app.js裏面來調用了。
require(['app/b'], function (b){ console.log(b.add(1,1)); });
咱們接着看看文件加載的狀況以下:
在head標籤內動態生成文件,以下:
能夠看到加載順序 requirejs --> app.js --> b.js。
上面的是函數式的定義如上面方式編寫代碼(使用define定義一個函數),咱們還能夠編寫簡單的鍵值對,直接返回一個對象(能夠解決全局變量的理念),咱們如今在a.js裏面返回這麼一個對象,以下:
// a.js define(function () { return { color: "black", size: "unisize" } }); //在app.js初始化代碼以下: require(['app/a'],function(a){ console.log(a); });
咱們在控制檯上能夠看到以下:
直接返回一個對象,經過使用上面的方法咱們能夠想到能夠解決全局變量概念,好比全局變量所有使用define函數包圍,何時須要全局變量的話,直接require([‘XX’],function(XX){})這樣調用下,同時全部的JS都是異步的,並不會堵塞加載。
3. AMD模塊規範
第一種寫法: define(function() { return { mix: function(source, target) { } }; }); 第二種寫法 有依賴項 以下: define(['data', 'ui'], function(data, ui) { // init here }); 第三種寫法 直接一個對象 define({ data: [], ui: [] }); 第四種寫法 具名模塊 以下: define('index', ['data','base'], function(data, base) { // todo }); 第五種寫法 包裝模塊 以下: define(function(require, exports, module) { var base = require('base'); exports.show = function() { // todo with module base } });
書寫格式和nodeJS比較像,可使用require獲取模塊,使用exports或者module.exports導出API。
固然requireJS是遵循AMD的規範的,因此通常狀況下也具備上面的書寫代碼方式。
對於第四種寫法 具名模塊寫法咱們並不推薦的,由於不書寫模塊名咱們同樣能夠調用,且在合併代碼的時候,咱們也能夠根據代碼自動生成模塊名,若是咱們如今寫死了模塊名,當某個時候,b.js我要移動到其餘目錄時候,JS也要跟着改,因此代碼維護方面很差,因此不建議書寫模塊名。對於第五種寫法,requireJS中也是支持的,經過內部調用require來處理依賴模塊,咱們也能夠試着作demo看看就知道了,仍是app.js,我想初始化a.js代碼,我改爲這樣的方式,以下代碼:
define(function(require, exports, module) { var a = require('app/a'); console.log(a); exports.show = function() { / / todo with module base } });
經過控制檯也能夠看到已經打印出 a 出來。
注意: 一、 書寫requireJS遵循一個文件一個模塊。
二、 不要手動寫模塊名標示。
4. requireJS配置項以下:
1.baseUrl: 指定本地模塊的基準目錄,即本地模塊的路徑是相對於那個目錄的,該屬性一般有requireJS加載時的data-main屬性指定。好比以下代碼:
項目目錄結構仍是上面的。
在頁面頂部<head>中引入 <script src="js/require.js" defer async="true" data-main="js/app"></script>
在app.js以下代碼:
requirejs.config({ baseUrl: 'js/app' }); requirejs(['a','b','c'],function(a,b,c){ });
在瀏覽器頁面遊覽能夠看到以下請求:
如上能夠看到,index.html和js是同一個目錄下的,都是放在requireJS文件夾裏面的,因此定義baseUrl:’js/app’ 會自動解析成 requireJS/js/app/ 因此requirejs([‘a’,’b’,’c’])的話,會自動到requireJS/js/app/目錄下去查找a.js,b.js,c.js.找到了就能夠加載出來。
若是未顯示設置baseUrl,則默認值是加載require.js的html所處的位置,若是使用了data-main屬性的話,則該路徑變成了baseUrl.以下代碼:
Index.html代碼以下:
<script src="js/require.js" defer async="true" data-main="js/app"></script>
App.js代碼以下:
requirejs(['a','b','c'],function(a,b,c){
});
那麼在瀏覽器下會被解析成以下:
如上顯示:默認狀況下是從data-main文件入口去加載js/app.js代碼的,可是如今app.js中並無設置config配置項,因此使用requirejs([‘a’,’b’,’c’],function(a,b,c))的時候會繼續加載js下面的a.js,b.js,c.js,若是找到就加載,沒有找到就顯示404 not found,如上所示。
2.paths: paths是映射那些不直接放在baseUrl指定的目錄下的文件,設置paths的起始位置是相對於baseUrl的,除非該path設置是以」/」開頭或含有URL協議(http://或者https://).
以下在app.js代碼:
requirejs.config({ baseUrl: 'js/lib', paths: app: '../app' } }); requirejs(['app/a'],function(a){ });
在頁面上加載顯示以下:
能夠看到paths是相對於baseUrl配置項生成的,baseUrl:’js/lib’下的全部js文件,可是paths下的 app:’../app’是相對於js/lib下設置的,’..’的解析到js目錄下,而後就解析成js/app下,再require([‘app/a’]),就解析到js/app/a.js了。
若是app.js代碼註釋掉baseUrl時,變成以下代碼:
requirejs.config({ //baseUrl: 'js/lib', paths: { app: '../app' } }); requirejs(['app/a'],function(a){ });
那麼就被加載成這個樣子了,以下所示:
直接把app/a.js放在項目文件requirejs下了。
3. shim參數 解決了使用非AMD方式定義的模塊(如jquery插件)及其載入順序,爲那些沒有使用define()來聲明依賴關係,設置模塊的」瀏覽器全局變量注入」型腳本作依賴和導出配置。
在js/app目錄下新建文件 depBase.js 代碼以下:
define(function(){ return { "a":11 } }) 接着在app.js文件裏面把代碼改爲以下: require.config({ baseUrl: 'js/lib', shim: { 'app/depBase': ['jquery'] }, paths: { app: '../app' } }); require(['app/depBase'],function(base){ console.log(base); });
而後在瀏覽器查看請求以下:
由上面能夠看到,我require(['app/depBase'],function(base){console.log(base);});這個,它先加載baseUrl中的配置 js/lib下的jquery文件,而後再加載js/app/depBase.js文件。也就是說shim這個參數能夠解決沒有使用define(function(){})這樣的文件包圍的代碼或者一些全局變量注入,能夠確保此文件先加載,而後再加載其餘文件。
可是若是我不使用shim這個參數的話,在最新版的requirejs2.1.15中(之前的版本我不太清楚),也能夠經過require([‘XX’])來解決,以下演示:
好比我在js/app文件下新建global.js文件,如今的目錄以下:
其中global.js代碼以下:
names = 1111;
創造一個全局變量names,其中js/app/depBase.js代碼變成以下:
define(function(){ return { 'name':names } })
也就是說我在app.js代碼以下初始化以下:
require.config({ baseUrl: 'js/app' }); require(['global','depBase'],function(global,base){ console.log(base); });
我先global初始化引入全局變量names,接着打印出depBase的返回值,截圖以下:
也能夠看到,能夠引入到全局變量names的值。
4.Map參數: Map參數是用來解決同一個模塊不一樣版本的問題,好比在項目開發中,開發初期使用了jquery1.7版本,可是因爲業務的需求須要引入jquery1.9以上的版本時候,可是又擔憂有些是依賴於jquery1.7的代碼升級到1.9以上的時候會有問題,所以可讓一部分代碼仍是依賴於jquery1.7,薪增的代碼依賴於jquery1.9.
下面咱們來看看咱們目錄結構以下所示:
我在lib文件下新增jquery1.7.js和 jquery1.9.1.js,如今我在入口文件app.js添加以下代碼:
requirejs.config({
map: {
'app/a': {
'jquery': 'js/lib/jquery1.7.js'
},
'app/b': {
'jquery': 'js/lib/jquery1.9.1.js'
}
}
});
require(['app/a'],function(jq){
});
require(['app/b'],function(jq){
});
而後在app/a.js添加以下代碼:
// a.js
define(function (require, exports, module) {
var a = require(['jquery']);
});
在app/b.js添加以下代碼:
// b.js
define(function (require, exports, module) {
var b = require(['jquery']);
});
在app.js中
require(['app/a'],function(jq){
});時候,在加載app/a.js的時候會加載jquery1.7.js文件,在加載app/b.js的時候會加載jquery1.9.1.js.以下截圖所示:
若是在app.js中把下面這行b.js代碼初始化註釋掉
require(['app/b'],function(jq){
});
那麼就只會加載app/a.js及對應的jquery1.7.js,截圖以下:
相應的 若是把app/a.js初始化代碼註釋掉,把app/b.js代碼初始化打開,那麼只會加載jquery1.9.1,能夠看到若是我想app/b.js中使用jquery1.9的話,那麼能夠這樣使用了。
5.config參數。 config是指須要將配置信息傳給一個模塊,這些配置每每是application級別的信息,須要一個手段將他們向下傳遞給模塊。在requireJS中,基於requirejs.config()的config配置項來實現。要獲取這些信息的模塊能夠加載特殊的依賴 」moudle」 ,並調用module.config().
首先咱們能夠仍是試着作demo來理解下上面話的意思吧,我如今在項目requirejs下js/app文件下新建一個d.js. 而後在app.js初始化文件加入以下代碼:
requirejs.config({ config: { 'app/c': { size: 'large' }, 'app/d': { color: 'blue' } } }); require(['app/c'],function(c){ console.log(c); }); require(['app/d'],function(dss){ console.log(d); }); 在c.js裏面這樣寫代碼: define(function (require, exports, module) { //其值是'large' var size = module.config().size; return size; });
在控制檯下運行能夠看到能打印出 large值出來,這說明咱們能夠經過config配置項來給app/c.js傳遞一個模塊信息,好比如上面的一個對象{size:large},而在c.js裏面直接能夠經過module.config()方法來獲取size的值。
下面咱們可使用一個依賴數組來作一樣的事情,以下d.js代碼:
define(['module'], function (module) { //Will be the value 'blue' var color = module.config().color; return color; });
在控制檯看 也同樣能夠打印出color值出來。
6. 內部機制:
RequireJS加載的每一個模塊做爲script Tag,使用head.appendChild()方法。
在模塊的定義時,requireJS等到全部的依賴都加載完畢,會爲函數的調用計算出正確的順序,而後在函數中經過正確的順序進行調用。
7. requireJS函數增長了第三個參數errbacks
仍是作demo來演示下,咱們仍是在入口文件app.js下增長代碼,以下:
原本加載b模塊是app/b 可是我故意寫錯成b 因此就不會執行第一個回調函數,轉而到第二個回調函數內。以下彈框:
8.在模塊載入失敗回調中可使用undef函數移除模塊的註冊。
以下代碼:
代碼: require(['b'], function ($) { //Do something with $ here }, function (err) { var failedId = err.requireModules && err.requireModules[0]; if (failedId === 'b') { requirejs.undef(failedId); } });