JS模塊化:CommonJS和AMD(Require.js)

早期的JS中,是沒有模塊化的概念的,這一狀況直到09年的Node.js橫空出世時有了好轉,Node.js將JS做爲服務端的編程語言,使得JS不得不尋求模塊化的解決方案。html

模塊化概念

在JS中的模塊是針對單個文件的,即一個文件是一個模塊,要使用這個模塊就加載該文件便可。node

CommonJS

node.js的模塊系統,是參照CommonJS規範實現的。編程

定義模塊和加載模塊

在CommonJS中,有一個全局性方法require(),用於加載模塊,而module.exports用於導出當前文件的模塊。數組

假定有一個外部模塊Utils.js,那麼該模塊須要這麼寫:瀏覽器

 1 // 定義類
 2 function Utils(name) {
 3     this._name = name;
 4 }
 5 
 6 // 定義類方法
 7 Utils.prototype.sayHi = function() {
 8     console.log("Hi, I am " + this._name);
 9 };
10 
11 // 定義靜態方法
12 Utils.add = function(a, b) {
13     return a + b;
14 }
15 
16 // 將類 Utils 做爲當前文件的模塊導出
17 module.exports = Utils;

加載模塊並使用的方法以下:服務器

1 // 加載外部模塊文件,位於當前文件夾下的 Utils.js,導入後該模塊放入變量 Utils 中
2 var Utils = require('./Utils.js');
3 
4 var obj = new Utils("Li Lei");
5 obj.sayHi(); // Hi, I am Li Lei
6 
7 console.log(Utils.add(10, 20)); // 30

能夠導出爲任意類型

咱們就上面的例子稍加改動,Utils.js以下:curl

 1 function Utils(name) {
 2     this._name = name;
 3 }
 4 Utils.prototype.sayHi = function() {
 5     console.log("Hi, I am " + this._name);
 6 };
 7 Utils.add = function(a, b) {
 8     return a + b;
 9 }
10 module.exports = {version: 0.1, utils: Utils};

執行的js代碼以下:異步

1 var ex = require('./Utils.js');
2 
3 console.log(ex.version); // 0.1
4 
5 var obj = new ex.utils("Li Lei");
6 obj.sayHi(); // Hi, I am Li Lei
7 
8 console.log(ex.utils.add(10, 20)); // 30

咱們能夠發現能夠導出任意的一個對象,而使用require導入的就是導出的那個對象。async

不適用於瀏覽器

在Node.js中,require方法是同步執行的,即只有加載並解析以後,代碼纔會向下執行,可是在瀏覽器中,加載腳本是異步執行的。編程語言

AMD

有了服務器端模塊之後,很天然地,你們就想要客戶端模塊。並且最好二者可以兼容,一個模塊不用修改,在服務器和瀏覽器均可以運行。

可是瀏覽器並不適用CommonJS規範,由於服務器端同步加載不是一個問題,全部的模塊都存放在本地硬盤,等待時間就是硬盤的讀取時間。可是,對於瀏覽器,這倒是一個大問題,由於模塊都放在服務器端,等待時間取決於網速的快慢,若是採用同步加載的策略,可能要等很長時間才能加載好文件,這段時間裏瀏覽器會處於"假死"狀態。

所以,瀏覽器端的模塊,不能採用"同步加載"(synchronous),只能採用"異步加載"(asynchronous)。這就是AMD規範誕生的背景。

概念

AMD是"Asynchronous Module Definition"的縮寫,意思就是"異步模塊定義"。它採用異步方式加載模塊,模塊的加載不影響它後面語句的運行。全部依賴這個模塊的語句,都定義在一個回調函數中,等到加載完成以後,這個回調函數纔會運行。

AMD也採用require()語句加載模塊,可是不一樣於CommonJS,它要求兩個參數:

require([module], callback);

第一個參數[module],是一個數組,裏面的成員就是要加載的模塊;第二個參數callback,則是加載成功以後的回調函數。

目前,主要有兩個Javascript庫實現了AMD規範:require.js和curl.js。下面咱們主要就require.js來看看如何在瀏覽器裏實現模塊化。

Require.js

官網地址:http://www.requirejs.cn/

這個是很是流行的一個實現AMD規範的JS庫,咱們下載其最新版本(目前是2.1.11),咱們下面用Require.js來實現上面CommonJS裏的例子。

定義模塊和加載模塊

AMD規範裏,定義模塊和加載模塊都和CommonJS規範不一樣,使用require加載模塊可是帶有回調函數,而定義模塊則使用define函數。

咱們先看看Utils模塊的寫法:

 1 define(function (){
 2     // 定義類
 3     function Utils(name) {
 4         this._name = name;
 5     }
 6 
 7     // 定義類方法
 8     Utils.prototype.sayHi = function() {
 9         console.log("Hi, I am " + this._name);
10     };
11 
12     // 定義靜態方法
13     Utils.add = function(a, b) {
14         return a + b;
15     }
16     
17     // 將類 Utils 做爲當前文件的模塊返回
18     return Utils;
19 });

加載和使用的方法以下:

 1 <html>
 2 <head>
 3     <title>Test</title>
 4     <!-- 引入require.js文件 -->
 5     <script src="require.js"></script>
 6     <script>
 7     // 配置各個模塊地址
 8     require.config({
 9     paths: {
10       "Utils": "./js/lib/Utils"
11     }
12   });
13 
14     // 若是模塊地址都放在同一個文件夾中,能夠用下面的簡寫方式
15     // require.config({
16     //     baseUrl: "./js/lib",
17   //   paths: {
18   //     "Utils": "Utils"
19   //   }
20   // });
21 
22     // 加載指定模塊
23     require(["Utils"], function(Utils) {
24         // 模塊加載完畢以後,模塊中導出的對象會做爲參數傳入,再回調中直接使用便可
25         
26         var obj = new Utils("Li Lei");
27         obj.sayHi(); // Hi, I am Li Lei
28 
29         console.log(Utils.add(10, 20)); // 30
30     });
31     </script>
32 </head>
33 <body>
34 </body>
35 </html>

多個模塊的狀況

咱們定義的模塊若是還依賴其它的模塊,能夠這麼寫:

1 // 當前定義的模塊依賴 Socket 模塊
2 define(["Socket"], function (){
3 // 當前定義的模塊依賴 Socket 及 Game 模塊
4 define(["Socket", "Game"], function (){

加載模塊時也能夠加載多個模塊:

1 // 加載 Utils 和 Game 模塊,加載好以後,對應模塊的導出對象會在回調中做爲參數分別傳入
2 require(["Utils", "Game"], function(Utils, Game) {

更多地用法,能夠參考官方文檔。

總結一下

咱們發現,CommonJS和AMD的寫法不管是定義模塊仍是加載模塊,都是存在差別的,可是模塊內部的寫法是基本一致的,因此經過必定的技巧,能夠寫出兼容兩種標準的模塊,以下:

 1 (function(golbal, factory){
 2     // AMD
 3     if(typeof define === "function" && define.amd)
 4         define(factory);
 5     // CommonJS
 6     else if(typeof require === "function" && typeof module === "object" && module && module.exports)
 7         module.exports = factory();
 8 })(this, function(){
 9     // 定義類
10     function Utils(name) {
11         this._name = name;
12     }
13 
14     // 定義類方法
15     Utils.prototype.sayHi = function() {
16         console.log("Hi, I am " + this._name);
17     };
18 
19     // 定義靜態方法
20     Utils.add = function(a, b) {
21         return a + b;
22     }
23     
24     // 將類 Utils 做爲當前文件的模塊返回
25     return Utils;
26 });

固然,知道了兩種標準的差別以後,咱們在使用時,也能夠本身修改一個標準的模塊爲另外一個標準能夠支持的寫法了。

另外,大部分類庫都會提供多種模塊格式的代碼,好比AMD和CommonJS等格式,選擇咱們須要的格式便可。

相關文章
相關標籤/搜索