2015年3月10日

使用RequireJS進行模組化Web開發


什麼是RequireJS?

RequireJS是JavaScript檔的載入工具。 它能夠定義JavaScript檔案之間的相依性,讓瀏覽器以正確的順序載入JavaScript。 此外,它亦實作了AMD(Asynchronous Module Definition) API, 讓網頁開發者能將JavaScript程式碼進行良好的模組化設計。

為何需要RequireJS?

大型Web應用程式開發者時常會面臨到JavaScript程式碼的維護問題:
  1. 為了實作複雜的商業邏輯,JavaScript程式碼寫的又大又多。
  2. 使用了大量的第三方套件,套件之間還有相依性。
一旦商業邏輯有所異動,或是套件版本有更新, 都是令Web應用程式開發者相當頭疼的。
因此,Web應用程式的模組化設計,以及第三方套件的管理工具, 都是開發Web應用程式時需要審慎評估的。 而RequireJS正是一個不錯的選擇。

What is AMD?

AMD API定義了一個define方法, 旨在希望網頁開發者在撰寫一支JavaScript程式時, 能夠使用define方法來將其定義為一個「模組」, 一來避免大量的變數暴露在全域, 二來能使JavaScript的開發更為「模組化」。
一般習慣下,一個JavaScript檔案會對應到一個模組,
define方法有三個參數:
define(id, dependencies, factory)
參數說明如下:
id:模組名稱。一般習慣下,一個JavaScript檔案會對應到一個模組;此時通常會省略id。
dependencies:相依的模組名稱列表,為字串陣列型態。表示載入此模組載入前,需要先載入的模組。
factory:將此模組初始化或實例化的函式。此函式需要有回傳值,通常是代表此模組的物件,以讓其他模組使用該模組的方法。此外, 此函式的參數列, 將依序應到dependencies陣列中的模組。
以下以範例說明AMD API的使用, 假設有moduleA.js與moduleB.js兩個js檔,分別定義了兩個模組,內容如下:
moduleA.js
/* moduleA.js */
define([],           //dependencies陣列
    function() {    //factory函式
        var ModuleA = {
            testExec : function() {
                console.log("into ModuleA testExec");
            }
        };
        return ModuleA;
    }
);
moduleB.js
/* moduleB.js */
define(["moduleA","module1","module2","module3"], //dependencies陣列
    function(ModuleA,Module1,Module2,Module3) {   //factory函式
            var ModuleB = {
                testExec : function() {
                console.log("into ModuleB testExec");
                ModuleA.testExec();
            }
        };
        return ModuleB;
    }
);
moduleA不相依於任何模組,dependencies陣列為空;
moduleB相依於moduleA,module1,module2,module3, 注意其factory函式的參數,將依照順序對應到dependencies陣列中的模組。
此外,moduleB.js使用了moduleA.js中所定義的物件之方法, 其關係如下:

首先注意moduleB的factory函式中有一個參數,名為ModuleA, 其對應到dependencies參數中的模組名稱"moduleA"。 預設情況下,RequireJS將自動依當前相對路徑去尋找moduleA.js, 並將moduleA.js中的factory函式的回傳值載入。
同理,moduleB.js同樣地也可以使用module1.js,module2.js,module3.js中所定義的物件,此處就不再贅述。

如何使用在HTML檔中使用RequireJS載入JavaScript?

了解AMD API的使用方式後, 以下將說明如何使用RequireJS在HTML檔案中載入JavaScript程式。
RequireJS本身也是一個JavaScript檔案,可以在RequireJS官方網站下載。
在HTML中使用RequireJS的方式如下:
<script data-main="js/main" src="path_to_requirejs"></script>
src是require.js檔的路徑。 data-main則指向RequireJS需要用到的設定檔,透過此設定檔,可以依據載入JavaScript模組並執行。該設定檔也是一個js檔案,且.js可以省略。 data-main設定檔最簡單的範例如下:
main.js
require(['moduleB'], function(ModuleB){
    ModuleB.testExec();
});


使用require.config的path參數設定模組別名

data-main設定檔中,還可以透過require.configpath參數設定模組的別名。這個參數尤其適用於是第三方JavaScript套件,如:
main.js
require.config({
    paths : {
        'jquery' : 'lib/jquery-1.9.0'
    }
});

require(['testloadlibs','jquery'], function(TestLoadLibs,JQuery){
    TestLoadLibs.testExec();
});
testloadlibs.js
define(["jquery"],          
    function(jquery) {      
        var TestLoadLibs = {
            testExec : function() {
                console.log("into TestLoadLib testExec");
                $(function(){
                    $(document.body).append("Hello jQuery!");
                });
            }
        };
        return TestLoadLibs;
    }
);

如此一來,其他模組在需要使用jQuery的時候,不僅不需要打上詳細的lib/jquery-1.9.0相對路徑;更重要的是:當jQuery版本需要更動時,也只要修改main.js,而不需要修改其他模組

結語

RequireJS不僅僅是JavaScript檔的載入工具,更是協助Web應用程式開發模組化以及管理第三方套件的利器。本文僅以最簡單的範例介紹RequireJS
事實上,光是require.config就提供了多種參數,以因應Web開發所可能遇到的各種情況,像是載入不支援以非AMD API開發的第三方JavaScript套件。 因此,RequireJS無疑是開發Web應用程式必學的套件之一。

Reference

初探RequireJS - 網站製作學習誌
Javascript模块化编程(三):require.js的用法