隨着前端JavaScript代碼愈來愈重,如何組織JavaScript代碼變得很是重要,好的組織方式,可讓別人和本身很好的理解代碼,也便於維護和測試。模塊化是一種很是好的代碼組織方式,本文試着對JavaScript模塊化開發的一些基礎知識和具體使用作一些闡釋。javascript
「模塊是爲完成某一功能所需的一段程序或子程序。模塊是任何robust(健壯、強壯)的應用架構不可缺乏的一部分,是系統中職責單一且可替換的部分。」html
簡單理解:模塊就是實現特定功能的一組方法,用用來實現代碼的封裝、加強代碼可重用性,知足代碼不斷維護升級,分工合做的須要。如何開發新的模塊,和複用已有模塊來實現應用的功能,是咱們須要考慮的事情,理想狀況下,開發者只須要實現核心的業務邏輯,其餘均可以加載別人已經寫好的模塊。前端
關於模塊,更多更詳細部分請參考:深刻理解JavaScript 模塊模式
java
JavaScript 的當前版本,並無爲開發者們提供以一種簡潔、有條理地的方式來引入模塊的方法(ECMAScipt第六版表示會支持)。node
做爲代替,當前的開發者們只能被迫降級使用模塊模式或是對象字面量模式的各類變體。經過不少這樣的方法,各模塊的腳本被串在一塊兒注入到 DOM 中(做爲 script
標籤注入到 DOM 中)。jquery
但好消息是在前端前輩們的不懈努力下,現在編寫模塊化的 JavaScript 目前已經變得極爲簡單,並摸索出一些廣泛適用性的標準:AMD、CommonJSgit
CommonJSgithub
wiki地址 http://wiki.commonjs.org/ajax
JavaScript是一個強大、流行的語言,它有不少快速高效的解釋器。官方JavaScript標準定義的API是爲了構建基於瀏覽器的應用程序。然而,並無定於一個用於更普遍的應用程序的標準庫。api
commonjs 是一個志願性質的工做組,它致力於設計、規劃並標準化 JavaScript API。從而填補原生JavaScript標準庫過少的缺點。它的終極目標是提供一個相似Python,Ruby和Java標準庫。它試圖覆蓋更寬泛的方面好比 IO、文件系統、promise 模式等等。這樣的話,開發者可使用CommonJS API編寫應用程序,而後這些應用能夠運行在不一樣的JavaScript解釋器和不一樣的主機環境中。如今很是火爆的nodejs實際上就是commonjs的一個實現。 ——CommonJS是一種規範,NodeJS是這種規範的實現。
在CommonJS中,使用全局性方法require()加載模塊。假定有一個數學模塊math.js,就能夠像下面這樣加載。
// 加載模塊 var math = require('math'); // 調用模塊提供的方法 math.add(2,3); // 5
注意,math.add(2, 3),在第一行require('math')以後運行,所以必須等math.js加載完成。也就是說,若是加載時間很長,整個應用就會停在那裏等。
這對服務器端不是一個問題,由於全部的模塊都存放在本地硬盤,能夠同步加載完成,等待時間就是硬盤的讀取時間。可是,對於瀏覽器,這倒是一個大問題,由於模塊都放在服務器端,等待時間取決於網速的快慢,可能要等很長時間,瀏覽器處於"假死"狀態。
所以,瀏覽器端的模塊,在網站性能優化正在逐步成爲產業的今天,不能採用"同步加載"(synchronous),只能採用"異步加載"(asynchronous)。這就是AMD規範誕生的背景。
AMD RequireJS介紹
AMD是「Asynchronous Module Definition」的縮寫,意思就是「異步模塊定義」。從名稱上就能夠看出,它是經過異步方式加載模塊的,模塊的加載不影響後續語句的執行,全部依賴加載中的模塊的語句,都會放在一個回調函數中,等到該模塊加載完成後,這個回調函數才運行。
它是適合script tag的,是專門爲瀏覽器中JavaScript環境設計的規範,它有不少獨特的優點,包括天生的異步及高度靈活等特性,這些特性可以解除常見的代碼與模塊標識間的那種緊密耦合。
RequireJS 是 AMD 規範最好的實現者之一,是一個很是小巧的 JavaScript 模塊載入框架。 從架構層抽象出「模塊化」開發方案,並已標準化了模塊化開發,同時和其餘的開發框架保持兼容。
主要用於瀏覽器端,但也適用於Rhino / Node 等環境,是當今最經常使用的JavaScript庫之一,它兼容全部主流瀏覽器。
IE 6+ .......... compatible ✔
Firefox 2+ ..... compatible ✔
Safari 3.2+ .... compatible ✔
Chrome 3+ ...... compatible ✔
Opera 10+ ...... compatible ✔
使用RequireJS,咱們可以更容易地實現更復雜,更強大的JS的富客戶端程序。
使用它咱們能夠解決兩個問題:
(1)實現js文件的異步加載,避免網頁失去響應;
(2)管理模塊之間的依賴性,便於代碼的編寫和維護。
它同時還起到了隱形命名空間的做用
注:不管當前 JavaScript 代碼是內嵌仍是在外鏈文件中,頁面的下載和渲染都必須停下來等待腳本執行完成。JavaScript 執行過程耗時越久,瀏覽器等待響應用戶輸入的時間就越長,雖然可使用async和defer關鍵字使得加載異步,但可能所以在加載過程當中丟失加載的順序。
使用require.js的第一步,是先去官方網站下載最新版本。
下載後,假定把它放在js子目錄下面,就能夠加載了。
<!DOCTYPE html> <html> <head> <title>My Sample Project</title> <!-- data-main屬性指定在require.js加載完成後,加載js/main.js文件. --> <script data-main="js/main" src="js/require.js"></script> </head> <body> <h1>My Sample Project</h1> </body> </html>
注意data-main屬性,因爲require.js默認的文件後綴名是js,因此能夠把main.js簡寫成main。
在mian中咱們就能夠很愉快地進行開發了,按照這種方式,整個頁面咱們只須要引入這一個js文件就能夠了。
咱們以使用jquery 爲例:
// 配置jquery 模塊的文件路徑 require.config({ paths: { jquery: 'jquery-1.8.3', underscore: 'underscore.min' // 能夠同時配置多個文件 } }); // 使用模塊 require(['jquery', 'underscore'], function($, _) { console.log($().jquery, _.VERSION); });
若是這些模塊在其餘目錄,好比js/lib目錄,能夠改變基目錄(baseUrl)。
require.config({ baseUrl: "js/lib", paths: { "jquery": "jquery.min", "underscore": "underscore.min" } });
paths參數配置 模塊名和路徑, 路徑能夠是遠程路徑:http:http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js 後綴名可省略。
若是jquery就在main.js文件同目錄下,則能夠省略配置的步驟,直接使用便可。
// 使用模塊 require(['jquery'], function($) { console.log($().jquery); });
AMD規範的API很是簡單:
define(id?, dependencies?, factory);
define
函數接受三個參數:
jquery
或者其餘用戶自定義模塊,沒有依賴的話能夠爲 [] ;例如: jQuery從1.7後開始支持AMD規範,即若是jQuery做爲一個AMD模塊運行時,它的模塊名是「jquery」。注意「jquery」 是小寫的。
jQuery中的支持AMD代碼以下:
if ( typeof define === "function" && define.amd && define.amd.jQuery ) { define( "jquery", [], function () { return jQuery; } ); }
一個完整的模塊定義包含模塊名稱,模塊的依賴和回調函數,好比下面的代碼:
define("adder", ["math"], function (math) { return { addTen : function (x) { return math.add(x, 10); } }; });
若是這個模塊並無依賴,那麼回調參數默認是 require,exports(一個空的輸出對象,回調函數沒有返回值的時候,默認返回 ), module( 模塊自身 ),這時模塊能夠改寫爲:
define("adder", function (require, exports) { exports.addTen = function (x) { return x + 10; }; });
若是省略第一個參數,則會定義一個匿名模塊,見代碼:
define( function (require, exports) { exports.addTen = function (x) { return x + 10; }; });
在實際中,使用的更多的是匿名模塊定義方式,由於這樣更加的靈活,模塊的標識和它的源代碼再也不相關,開發人員能夠把這個模塊放在任意的位置而不須要修改代碼。通常只有在要使用工具打包模塊到一個文件中時,纔會聲明第一個參數,因此應該儘可能避免給模塊命名。
在寫模塊的時候,也有可能沒有依賴或者稍後才須要加載依賴
define(function (require, exports, module) { // …… var a = require('a'), b = require('b'); exports.action = function () { // …… }; });
上述回調函數裏的require的使用將被自動進行動態加載。