以前一直有據說RequireJS,可是一直都沒機會去了解,只知道它是一個給js作模塊化的API。最近在作React,其組件化的思想和js模塊化的思想不謀而合。就想在項目中應用React的同時,也把RequireJS加進來,看看會不會對頁面加載或者開發有很好的效果。javascript
在說明什麼是RequireJS以前,不得不提的就是Javascript模塊化歷史的背景。其實在早期,javascript做爲一門新興的腳本語言出現,有着龐大的願景,它並非做爲一門僅僅針對客戶端設計的語言。只是說後來web應用的流行,javascript做爲瀏覽器端腳本語言而迅速傳開,加上Netscape和微軟的競爭將其過早的標準化。因此就致使了JS的諸多缺陷,其中一個就是模塊化(可是你能夠驚奇地發現其實javascript有將import,export等做爲保留字,說明設計的時候實際上是有考慮的,新的標準es6也讓原生支持模塊化了)。而後隨着web應用愈來愈複雜,嵌入的javascript代碼愈來愈多,還有node的興起,模塊化編程就變成了必須。html
因此就有了後來Dojo工具包和Google的Closure庫支持的模塊系統。還有兩個很是通用的標準規範,CommonJS和AMD。這裏就不展開說了,咱們只須要知道,實現CommonJS規範的API是同步加載模塊的,而實現AMD規範的API是則是異步加載模塊。
因此理論上來講,AMD規範的非阻塞加載更加適合瀏覽器端。而RequireJS就是AMD規範的最好實現。抄一段官方文檔對RequireJS的描述:java
RequireJS 是一個JavaScript模塊加載器。它很是適合在瀏覽器中使用, 它很是適合在瀏覽器中使用,但它也能夠用在其餘腳本環境, 就像 Rhino and Node. 使用RequireJS加載模塊化腳本將提升代碼的加載速度和質量。node
因此,知道了RequireJS是幹什麼的,也差很少知道爲何咱們要使用RequireJS了。不過仍是總結一下用RequireJS的好處吧。jquery
<script data-main="js/main" src="xxx/xxxx/require.js"></script>
使用RequireJS,你只須要引入一個require.js便可。須要說明的是,一個比較好的實踐,就是你的頁面上面應該也只須要經過\<script\>標籤引入這一個js便可。而後你這個頁面的全部業務邏輯只須要在main.js裏面寫(data-main屬性做用後面會有講)就能夠了。其它引用的依賴怎麼辦?固然是經過require按需引入啊!git
其實Requirejs整個源文件包括註釋就2000來行,其對外暴露的變量其實就三個,requirejs,require,define。es6
這其中requirejs 只是require的一個別名,目的是若是頁面中有require其它實現了,你仍是能經過使用requirejs來使用requireJS API的(本文中沒有相關衝突,因此仍是使用require)。github
因此這意味着做爲入門,你只須要掌握require,require.config,define這三樣就能夠了。web
本文將以介紹require,require.config,data-main,define的順序來介紹RequireJS。讓比較簡單的RequireJS更加簡單,爭取讓你們只看這篇文章就能用好RequireJS。至於RequireJS是如何解決循環依賴,對於沒有實現amd的模塊如何經過shim來導出,如何在node中使用等問題。本文並無說起,詳細有須要能夠去官方查閱。npm
首先,先無論三七二十一,咱們先按照下面的方式建立一個這樣的目錄結構:
而後require.js能夠經過npm下載或者在官網得到。jquery同理,jquery須要下載1.7.0或以上的版本。而後把對應的代碼拷入對應的文件中,給出餘下兩個文件的代碼:
// js/script/index.html
<!DOCTYPE html> <html> <head> <title>Require Demo 1</title> </head> <body> <div> <h1>Require Demo 1 -- usage of Require()</h1> <button id="contentBtn">Click me</button> <p id="messagebox"></p> </div> <script data-main="js/script/main" src="js/lib/require.js" type="text/javascript"></script> </body> </html>
// js/script/main.js require.config( { paths: { 'jquery': '../lib/jquery-1.7.2' } } ); require(['jquery'],function ($) { $(document).on('click','#contentBtn',function(){ $('#messagebox').html('You have access Jquery by using require()'); }); });
先看index.html的代碼,其實比較簡單,頁面上在js中會用到的就是一個button和一個p標籤。而後整個頁面就只是一個js文件是經過\<script\>標籤加載的,就是require.js。注意到標籤中有一個data-main屬性,你如今只須要了解require.js會在加載完成之後經過回調方法去加載這個data-main裏面的js文件,因此這個js文件被加載的時候,RequireJS已經加載執行完畢。
而後接着看main.js文件,裏面被一個匿名當即執行函數所包括。在require.config(...)中,能夠配置許多配置項,後面會有詳細說明。上面在config中添加了一個path,在path配置了一個模塊ID和路徑的映射,這樣在後續的全部函數中就能夠直接經過模塊ID來引入依賴,而不用再屢次引入依賴屢次輸入路徑帶來的麻煩。
而後接着就是咱們的require(...)函數了。上面的語法中require函數接受的第一個參數是,所依賴模塊的一個數組。即便你只須要傳入一個依賴,也須要把這個依賴放進數組中傳入。若是你有如本例子中設置了模塊ID和路徑的映射,那你在傳入依賴的時候就可使用模塊ID來代替路徑,若是沒有配置模塊ID你固然也能夠經過路徑來引進對應的模塊。接着是傳入回調函數,當引入的依賴加載完畢後,這個回調函數就會被觸發。若是你傳入的依賴有注入變量(函數),而後在回調函數中須要用到,你就須要按照順序在回調函數的參數中添加別名,在本例子中能夠經過別名$來使用jQuery的相關API。因此有注入的模塊須要放在無注入或者不須要調用模塊的模塊前面,方便回調函數傳入別名。例子中在回調函數中爲id爲contentBtn的button註冊監聽事件,若是觸發,則往id爲messagebox的p標籤添加相應的內容。
另外還須要額外說明的是路徑,不論是在配置中寫路徑仍是直接在require函數中寫路徑,你都須要瞭解requireJS在不一樣狀況下的相對路徑。
如下是相對路徑的規則:
1.若是\<script\>標籤引入require.js時沒有指定data-main屬性,則以引入該js的html文件所在的路徑爲根路徑。
2.若是有指定data-main屬性,也就是有指定入口文件,則以入口文件所在的路徑爲根路徑。在本例子中也就是main.js所在的script文件夾就是根路徑,這也是爲何配置jQuery的時候須要返回上層目錄再進入lib目錄才能找到jQuery文件。
3.若是再require.config()中有配置baseUrl,則以baseUrl的路徑爲根路徑。
以上三條優先級逐級提高,若是有重疊,後面的根路徑覆蓋前面的根路徑。
打開網頁,而後你就應該看到這樣的頁面:
點擊按鈕,有以下效果,說明經過RequireJS已載入Jquery,而且經過Jquery綁定了監聽事件。
講完了如何引入模塊,如今講如何定義一個模塊,require定義一個模塊是經過 define = function (name, deps, callback)完成的,第一個參數是定義模塊名,第二個參數是傳入定義模塊所須要的依賴,第三個函數則是定義模塊的主函數,主函數和require的回調函數同樣,一樣是在依賴加載完之後再調用執行。
先看個例子:
// js/script/desc.js define(function(){ return{ decs : 'this js will be request only if it is needed', }; })
// 而後在main.js的添加以下代碼 // js/script/main.js $('#messagebox').html('You have access Jquery by using require()'); + require(['script/desc'],function(desc){ + alert(JSON.stringify(desc));
再次打開網頁,打開network視圖,點擊按鈕,經過require得到的desc模塊就會alert出來,同時你會發現,desc.js是按需請求的,並非在頁面一開始的時候就請求的。
// js/script/alertdesc.js define(['script/desc'],function(desc){ return function (){ alert(JSON.stringify(desc)); }; })
// 而後在main.js的再作以下修改 // js/script/main.js $('#messagebox').html('You have access Jquery by using require()'); - require(['script/desc'],function(desc){ - alert(JSON.stringify(desc)); + require(['script/alertdesc'],function(alertdesc){ + alertdesc();
若是你細心,你可能會發現,剛剛define函數,有一個參數name是用來定義模塊名的(也就是第一個傳參),爲何上面兩個例子都沒有用到。其實我確實能夠添加模塊名,以下:
// js/script/alertdesc.js define(['script/desc'],function(desc){ ..... }) //Change To define('/script/alertdesc',['script/desc'],function(desc){ ..... })
可是,這樣作感受不頗有必要,由於若是哪一天我將這個文件轉移到其餘目錄下,那我就得在這這裏再修改一次模塊名。官方其實也不推薦,用官方的說法是:讓優化工具去自動生成這些模塊名吧!
在上面一節介紹require()函數的時候,咱們已經接觸過require.config(...)了。其實說白了,在require.config()作的一些修改會影響到全局require的一些特性。如上面的,你設置了baseUrl
,其require的根路徑就以這個路徑爲準,你在path中設置了模塊ID與路徑的映射,後面須要用到相關模塊的時候直接使用模塊ID代替路徑就行了,設置map能夠在不一樣路徑下用相同的模塊ID調用不一樣版本的模塊。
其實這裏並不打算對require.config()的具體配置展開來介紹,有須要能夠直接去官網查閱相關配置信息加進來就行了。
始終以爲require.config()應該抽出來,單獨放在一個js文件裏面,這樣方便移植和重用。在github上看了些例子,找到一個比較好的放置require.config的地方,放在這裏能夠參考:
// 添加config.js // js/script/config.js define(function(){ require.config({ baseUrl: './js/', paths: { 'jquery': 'lib/jquery-1.7.2' } }); });
// 替換main.js // js/script/main.js require(['config'],function(){ require(['jquery'],function ($) { $(document).on('click','#contentBtn',function(){ $('#messagebox').html('You have access Jquery by using require()'); require(['script/alertdesc'],function(alertdesc){ alertdesc(); }); }); }); });
還記得剛剛的\<script\>引入RequireJS時標籤中有一個data-main屬性麼?當require.js加載的時候會檢查data-main屬性,因此你能夠在data-main指向的腳本(也就是本例子中的js/main.js)中設置模塊加載的選項,而後在這個腳本加載第一個應用模塊。注意,你在main.js中所設置的腳本是異步加載並經過回調來執行的,這意味着若是你在頁面中有經過\<script\>引入其它的腳本,那不能保證在main.js裏面作的配置會在其它腳本中生效。
例如:
<script data-main="scripts/main" src="scripts/require.js"></script> <script src="scripts/other.js"></script>
// scripts/main.js: require.config({ paths: { foo: 'libs/foo-1.1.3' } });
// scripts/other.js:
// 因爲main.js會是在require.js異步加載完之後再經過回調去執行main.js的 // 因此other.js裏面執行的這個require函數可能會發生在main.js的require.config執行以前 // 所以require.config會去嘗試去加載"scripts/foo.js",而不是"scripts/libs/foo-1.1.3.js" require( ['foo'], function( foo ) { });
這個例子是官方的。從這裏也能夠看出來,爲何如前文所說的。頁面中最好只有一個入口點文件(屬性data-main中引入的main.js),而後這個入口點文件裏引入或者編寫配置,加載相關應用模塊。
固然你也能夠像官方給的第二種方案,不設置入口點,而後在每一個require回調中再引入相關配置,不過那樣很麻煩並且不易於維護。這裏就不給出例子了,有須要能夠去官網看。
以上就是關於關於RequireJS簡單使用的介紹了,你們有須要能夠直接看源碼,大概就2000多行,不看具體實現,看它對幾個函數聲明的描述,對使用起來也是頗有幫助的,你會發現有一些連官方文檔都沒說起到的一些特性(好比require()方法能夠直接傳入config配置做爲第一個參數)。
另外,說一點小插曲,若是須要查閱RequireJS官方的API,有條件的仍是建議直接訪問英文官方文檔。若是說中文的官方文檔說還停留在老版本,翻譯得比較生澀難懂就算了。一些很明顯有錯誤的描述就真的是責任問題了。我在看中文文檔的時候真是各類難移理解,後來直接看英文文檔,則順暢不少。很少說,貼張圖讓你們感覺一下英文文檔和中文文檔對於waitSeconds的描述: