早期的JS中,是沒有模塊化的概念的,這一狀況直到09年的Node.js橫空出世時有了好轉,Node.js將JS做爲服務端的編程語言,使得JS不得不尋求模塊化的解決方案。html
在JS中的模塊是針對單個文件的,即一個文件是一個模塊,要使用這個模塊就加載該文件便可。node
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方法是同步執行的,即只有加載並解析以後,代碼纔會向下執行,可是在瀏覽器中,加載腳本是異步執行的。編程語言
有了服務器端模塊之後,很天然地,你們就想要客戶端模塊。並且最好二者可以兼容,一個模塊不用修改,在服務器和瀏覽器均可以運行。
可是瀏覽器並不適用CommonJS規範,由於服務器端同步加載不是一個問題,全部的模塊都存放在本地硬盤,等待時間就是硬盤的讀取時間。可是,對於瀏覽器,這倒是一個大問題,由於模塊都放在服務器端,等待時間取決於網速的快慢,若是採用同步加載的策略,可能要等很長時間才能加載好文件,這段時間裏瀏覽器會處於"假死"狀態。
所以,瀏覽器端的模塊,不能採用"同步加載"(synchronous),只能採用"異步加載"(asynchronous)。這就是AMD規範誕生的背景。
AMD是"Asynchronous Module Definition"的縮寫,意思就是"異步模塊定義"。它採用異步方式加載模塊,模塊的加載不影響它後面語句的運行。全部依賴這個模塊的語句,都定義在一個回調函數中,等到加載完成以後,這個回調函數纔會運行。
AMD也採用require()語句加載模塊,可是不一樣於CommonJS,它要求兩個參數:
require([module], callback);
第一個參數[module],是一個數組,裏面的成員就是要加載的模塊;第二個參數callback,則是加載成功以後的回調函數。
目前,主要有兩個Javascript庫實現了AMD規範:require.js和curl.js。下面咱們主要就require.js來看看如何在瀏覽器裏實現模塊化。
這個是很是流行的一個實現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等格式,選擇咱們須要的格式便可。