隨着的互聯網技術的不斷髮展,瀏覽器逐漸成了集大成的CS客戶端,頁面功能在向系統級、軟件級靠攏的趨勢下,開發團隊也須要一些軟件工程的方法來開發WEB項目,如測試驅動、面向對象,而模塊化編程更是成爲一種急需應用的技術。javascript
爲方便文件管理、增長複用,咱們須要細化JS文件,每一個文件負責單一職能,稱之爲模塊,明確每一個文件的職能,當交互功能較複雜時,引用的文件也越加龐大,此時咱們就須要模塊化管理。css
如咱們編寫一個模塊組件Dialog.js,其依賴event.js / bootstrap.js / component.js,而event.js又依賴 base.js / core.js / el.js,bootstrap.js依賴 boot.js / under.js / almond.js,同理可能依賴更多的js,傳統方法咱們用到Dialog.js時實際上須要引用9個js,以下html
<html> <body> ... <script src="/public/base.js"></script> <script src="/public/core.js"></script> <script src="/public/el.js"></script> <script src="/public/boot.js"></script> <script src="/public/under.js"></script> <script src="/public/almond.js"></script> <script src="/module/event.js"></script> <script src="/module/bootstrap.js"></script> <script src="/module/component.js"></script> <script src="/widget/dialog.js"></script> </body> </html>
咱們將其細分,Dialog所依賴的每一個js都負責了某一獨立功能,分開便於其它組件複用,也利於學習理解。java
但將這9個js都到html裏顯示引用,工做量卻很是可觀,且不能弄錯路徑及加載順序,若後續Dialog.js又須要多引用一個js,全部的項目都要手動增長,且這樣依次同步加載下來會致使會佔用瀏覽器線程,可能致使假死。node
模塊化的做用就是解決上面提到的兩點問題。jquery
一、實現js文件的異步加載,避免瀏覽器假死。
二、管理模塊之間的依賴,便於模塊的維護git
模塊化,就是把一個相對獨立的功能,單獨成一個文件,可輸入指定依賴、輸出指定的函數,供外界調用,其它都是內部的。這樣便可方便不一樣的項目重複使用,也不會對其形成額外的影響。而既然要給不一樣的項目應用,特別像git上的模塊是面向全球範圍的,必然要造成可遵循的規範,開發者和使用者都有據可尋。github
目前通用javascript規範分爲服務端(CommonJS),客戶端(AMD / CMD)。shell
CommonJS是針對javascript的服務端,一切模塊都是經過require來同步加載調用, Nodejs採用的就是這個規範。編程
var module1 = require( 'module1'), module2 = require( 'module2' ); function module3(){ ... } exports.module3 = module3;
注:經過require加載module一、2,操做後經過exports致使module3。
這裏咱們主要講客戶端模塊
客戶端與服務端的不一樣在於異步加載,之因此異步是由於CommonJS規則應用在服務端時,文件都是本地硬盤讀取,不受限於網絡,等待硬盤讀取時間很短。而瀏覽器要考慮網速,若長時間等待會致使瀏覽器假死。
引用形式以下圖
是"Asynchronous Module Definition"的縮寫,意思就是"異步模塊定義",
以require.js爲表明,官網http://requirejs.org/ ,其形式主要是
=============main.js=================== <script src="js/require.js" data-main="js/Dialog"></script> ==============Dialog.js======================= define( ['event', 'bootstrap', 'component' ], function( event, bootstrap, component ){ event.doSomthing(); bootstrap.doSomthing(); component.doSomthing(); var Dialog = function(){ ... } return { Dialog: Dialog } });
注:require接收兩個參數,第一個參數是數組,含需加載的模塊,第二個參數是回調,在模塊加載成功後執行,且回調中的參數對應模塊的加載順序,這樣主程序只須要加載一個JS文件,Dialog.js便可,其自會去加載event.js等文件,event.js再用一樣的方式去加載base.js等,使用者無需考慮Dialog加載了多少引用,也沒法擔憂其使用後會增減依賴。
以sea.js爲表明,官網 http://seajs.org/docs/,理念爲須要時再執行,即依賴就近
=============main.js=================== <script src="js/sea.js"></script> <script>seajs.use("../static/hello/src/main")</script> ==============Dialog.js======================= define( function( require, exports, module ){ var event = require( 'event' ); event.doSomthing(); var bootstrap = require( 'bootstrap' ); bootstrap.doSomthing(); var component = require( 'component' ); component.doSomthing(); var Dialog = function(){ ... } exports.Dialog = Dialog; })
注:模塊引用自己就在回調裏,而且在使用前引用便可,因此sea.js強調的是懶加載,這也就是AMD和CMD之間惟一的區別。實際上,require.js也支持CMD的寫法,不過依賴前置是其官方推薦。我最初由於對國產和玉伯的支持敬佩,使用了seajs,近來爲了弄清楚AMD,項目所有用了requirejs,雖然網上及seajs官方對兩種邏列了一些分別,但使用下來,感受並沒有優劣。
因服務端與客戶端對模塊化加載方式的不一樣,就會人想作一個兼容的模塊UMD(Universal Module Definition),來解決跨平臺的問題。但對於咱們開發者來說實則沒有什麼意義,也不深刻了。
require.config({ baseUrl: 'module/js', //配置path下的統一目的,以便path中不會分別配置 path: { //配置module路徑,以便引用時不用寫全路徑 module1: 'module1', module2: 'module2', module3: 'http://www.site.com/module3' //若引用的是第三方,能夠所有全路徑 }, shim: { //對不符合AMD規則的模塊引用 'underscore': { exports: '_' //配置其原對外接口 }, 'backbone': { deps: ['underscore', 'jquery'], //配置其依賴項 exports: 'Backbone' } } }); define( ['module1', 'module2' ], function( module1, module2 ){ module1.doSomthing(); module2.doSomthing(); function module3(){ ... } return { module3: module3 } })
require.js沒有找到可配置時間戳,可修改require.js源文件中的load方法
load: function (id, url) { url += '?' + timestamp; req.load(context, id, url); },
新增 url += '?' + timestamp; 這一處語句,即爲url添加timestamp,timestamp能夠另外經過配置文件配置。我使用nodejs開發,在服務端的config文件配置 timestamp: '2015091112',將其插入到客戶端 ejs文件內
<script> var timestamp = '<%= timestamp %>'; </script>
這樣便可在 require.js 文件內調用。
官網提供了r.js用於該模塊化的打包 https://github.com/jrburke/r.js
r.js是將經過require.js引用的模塊化文件都合併到一個文件裏,以減小文件加載數量。
項目參考 https://github.com/xiaolulu/mynodejs.git| d7a0f54705a10ac329dd2f62a7d569da2 |
首先下載r.js,能夠到靜態文件根目錄,新建build.js用於配置r.js,常見的配置以下
build.js ({ appDir: './', //項目根目錄,給後面的baseUrl作參考;非必要項;默認爲與r.js平級目錄 dir: './min', //壓縮後的文件的輸出目錄 baseUrl: './', //模塊相對目錄,與後面的paths配置使用 modules: [ { name:'login'}, { name: 'register' }, { name: 'center' } ], fileExclusionRegExp: /^(r|build)\.js|.*\.scss$/, //過濾文件不壓縮 optimizeCss: 'standard', //css的標準方式 optimize: 'uglify', //設置是否壓縮;uglify(壓縮格式)|none(只合並不壓縮) removeCombined: true, //設置移除壓縮後的文件 paths: { //設置模塊別名,方便引用,壓縮後require.js引用的模塊路徑就在這裏定義的,不是require.config裏的 md5: 'core/js/md5', validate: 'widget/validate/validate', all: 'public/js/all', login: 'module/issue/login/login', register: 'module/issue/register/register', center: 'module/user/center/center' } })
shell執行 node r.js -o build.js
合併後全部原經過模塊化加載的文件都已經在一個文件裏了,已經不須要require.js的大部分功能,但因合併後代碼裏仍是有require / define的函數定義,這裏命令解析仍是要有的,因此就有了almond.js(約15KB),其保留了require.js(約80KB)的子集,但不支持模塊化加載,即對已經合併後的模塊解析可使用,減小代碼量。
不過,其理念是好的,但實現方案還待改進,其執行後就會almond.js與前面合併後過的業務JS合併成一個文件,直接用 src來引用,意味着每一個頁面都須要的 almond.js將沒法緩存,因此當業務JS超過五個時,這個方案將沒有意義,有興趣能夠自行到git上學習,這裏不在講述 https://github.com/jrburke/almond