前端模組化——RequireJS
為什麼模組很重要?
因為有了模組,我們就可以更方便地使用別人的程式碼,想要什麼功能,就載入什麼模組。
但是,這樣做有一個前提,那就是大家必須以同樣的方式編寫模組。目前,通行的Javascript模組規範共有兩種:CommonJS和AMD。
AMD是"Asynchronous Module Definition"的縮寫,即"非同步模組定義"。它採用非同步方式載入模組,模組的載入不影響它後面語句的執行。所有依賴這個模組的語句,都定義在一個回撥函式中,等到載入完成之後,這個回撥函式才會執行。
目前,主要有兩個Javascript庫實現了AMD規範:require.js和curl.js。
RequireJS是一個非常小巧的JavaScript模組載入框架,是AMD規範最好的實現者之一。它可以和其他的框架協同工作,使用RequireJS必將使您的前端程式碼質量得以提升。
以前載入js檔案是這樣的:
<script src="a.js"></script> <script src="b.js"></script> <script src="c.js"></script> <script src="d.js"></script> <script src="e.js"></script> <script src="f.js"></script> <script src="g.js"></script> <script src="h.js"></script> <script src="i.js"></script> <script src="j.js"></script>
這樣寫有很多缺點:
1、載入時,瀏覽器會停止網頁渲染,載入檔案越多,網頁失去響應時間就會越長;
例如:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="index.js"></script> </head> <body> <div>body</div> </body> </html>
index.js
(function(){
alert("hello");
}());
開啟瀏覽器,發現頁面先彈出彈框,然後一直在載入,不點選確定,頁面載入不出來,只有點選確定以後,頁面內容才會加載出來。
2、由於js檔案之間存在依賴關係,因此必須嚴格保證載入順序(比如上例的a.js要在b.js的前面),依賴性最大的模組一定要放到最後載入,當依賴關係很複雜時,程式碼編寫和維護就會變得困難。
require.js的誕生,就是為了解決這兩個問題:
1、實現js檔案的非同步載入,這樣就可以防止js載入阻塞頁面渲染,導致網頁失去響應;
2、管理模組之間的依賴性,便於程式碼的編寫和維護。
RequireJS的用法:
一、引入require.js檔案
為了避免載入這個檔案,也可能造成網頁失去響應。可以把它放在網頁底部載入,或是:
<script src="require.js" defer async="true"></script>
async屬性表明這個檔案需要非同步載入,避免網頁失去響應。IE不支援這個屬性,只支援defer,所以把defer也寫上。
二、載入自己js檔案
<script src="require.js" data-main="main"></script>
data-main屬性的作用是,指定網頁程式的主模組。
上面網頁程式的主模組就是main.js,意思就是,整個網頁的入口程式碼。這個檔案會第一個被require.js載入。由於require.js預設檔案字尾名是js,所以可以把main.js簡寫成main。
三、書寫主模組 main.js:require()函式
主模組依賴於其他模組,這時就要使用AMD規範定義的的require()函式。
語法:
require(['module1','module2','module3', ...],function(module1,module2,module3, ...){
//主模組的程式碼就寫在回撥函式中
});
第一個引數:一個數組,表示所依賴的模組,即其他的js檔案。第二個引數:一個回撥函式。
require()非同步載入其他模組,只有當前面指定的模組都載入成功後,回撥函式才會被呼叫。載入的模組會以引數形式傳入該回調函式,從而在回撥函式內部就可以使用這些模組。
四、模組的載入:require.config()方法
如果主模組所依賴的其他模組跟main.js在同一目錄下,第三步的寫法,模組就會自動載入。但是,很多情況下,並不是所有模組都在一個目錄下,這就需要使用require.config()方法,可以對模組的載入行為進行自定義。
require.config()就寫在主模組(main.js)頭部。引數是一個物件,這個物件的paths屬性指定各個模組的載入路徑。
例如:main.js依賴js目錄下的三個檔案,有兩種寫法
1、逐一指定路徑。
require.config({
paths:{
"food":"js/food",
"snake":"js/snake",
"game":"js/game"
}
});
2、直接改變基目錄(baseUrl)。
require.config({
baseUrl:"js",
paths:{
"food":"food",
"snake":"snake",
"game":"game"
}
});
載入方法:
require(['food','snake','game'],function(food,snake,game){
//主模組程式碼
});
如果某個模組在另一臺主機上,也可以直接指定它的網址,比如:
require.config({
paths: {
"jquery": "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min"
}
});
require.js要求,每個模組是一個單獨的js檔案。這樣,如果載入多個模組,就會發出多次HTTP請求,會影響網頁的載入速度。因此,require.js提供了一個優化工具 r.js。
當模組部署完畢以後,可以用 r.js優化頁面中的js指令碼和css檔案,以提高頁面響應速度,減少頁面http/https請求次數。
r.js的優化分為兩種方式:一是壓縮js和css檔案,也就是去掉空格,空行,將長變數名換成短變數名之類的;二是合併多個js檔案為一個js檔案,合併多個css檔案為一個。
五、AMD模組的寫法:define()函式
require.js載入的模組,採用AMD規範。即:模組必須按照AMD的規定來寫。就是模組必須採用特定的define()函式來定義。
如果一個模組不依賴其他模組,可直接定義在define()函式中。
假定有一個math.js檔案,定義了一個math模組。那麼,math.js就要這樣寫:
// math.js
define(function (){
var add = function (x,y){
return x+y;
};
return {
add: add
};
});
如果一個模組還依賴其他模組,那麼define()函式的第一個引數,必須是一個數組,指明該模組的依賴性。
define(['math'], function(math){
var result = function (x,y){
return math.add(x,y)*math.add(x,y);
};
return {
result: result
};
});
當require()函式載入上面這個模組的時候,就會先載入math.js檔案。
六、載入非規範的模組:shim屬性
有一部分流行的函式庫(比如jQuery)符合AMD規範,但是也有更多的庫並不符合。那麼,require.js是怎樣載入非規範的模組呢?
非規範模組在用require()載入之前,要先用require.config()方法,定義它們的一些特徵。
require.config()接受一個配置物件,這個物件除了有paths屬性外,還有一個shim屬性,專門用來配置不相容的模組。
非規範模組要定義(1)exports值(輸出的變數名),表明這個模組外部呼叫時的名稱;(2)deps陣列,表明該模組的依賴性。
比如,jQuery的外掛可以這樣定義:require.config( shim: { 'jquery.scroll': { deps: ['jquery'], exports: 'jQuery.fn.scroll' } } );七、require.js外掛
require.js還提供一系列外掛,實現一些特定的功能。
domready外掛,可以讓回撥函式在頁面DOM結構載入完成後再執行。
text和image外掛,允許require.js載入文字和圖片檔案。
json和mdown外掛,用於載入json檔案和markdown檔案
參考資料: