JS模塊化編程總結_待續

1、背景

隨着的互聯網技術的不斷髮展,瀏覽器逐漸成了集大成的CS客戶端,頁面功能在向系統級、軟件級靠攏的趨勢下,開發團隊也須要一些軟件工程的方法來開發WEB項目,如測試驅動、面向對象,而模塊化編程更是成爲一種急需應用的技術。javascript

2、緣由

爲方便文件管理、增長複用,咱們須要細化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

3、做用

模塊化的做用就是解決上面提到的兩點問題。jquery

一、實現js文件的異步加載,避免瀏覽器假死。
二、管理模塊之間的依賴,便於模塊的維護git

4、實現

模塊化,就是把一個相對獨立的功能,單獨成一個文件,可輸入指定依賴、輸出指定的函數,供外界調用,其它都是內部的。這樣便可方便不一樣的項目重複使用,也不會對其形成額外的影響。而既然要給不一樣的項目應用,特別像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規則應用在服務端時,文件都是本地硬盤讀取,不受限於網絡,等待硬盤讀取時間很短。而瀏覽器要考慮網速,若長時間等待會致使瀏覽器假死。

引用形式以下圖

amd pic

5、AMD

是"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加載了多少引用,也沒法擔憂其使用後會增減依賴。

6、CMD

以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官方對兩種邏列了一些分別,但使用下來,感受並沒有優劣。

7、UMD

因服務端與客戶端對模塊化加載方式的不一樣,就會人想作一個兼容的模塊UMD(Universal Module Definition),來解決跨平臺的問題。但對於咱們開發者來說實則沒有什麼意義,也不深刻了。

8、require.js 使用

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
     }
 })

9、require.js 時間戳

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 文件內調用。

10、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

11、almond

合併後全部原經過模塊化加載的文件都已經在一個文件裏了,已經不須要require.js的大部分功能,但因合併後代碼裏仍是有require / define的函數定義,這裏命令解析仍是要有的,因此就有了almond.js(約15KB),其保留了require.js(約80KB)的子集,但不支持模塊化加載,即對已經合併後的模塊解析可使用,減小代碼量。

不過,其理念是好的,但實現方案還待改進,其執行後就會almond.js與前面合併後過的業務JS合併成一個文件,直接用 src來引用,意味着每一個頁面都須要的 almond.js將沒法緩存,因此當業務JS超過五個時,這個方案將沒有意義,有興趣能夠自行到git上學習,這裏不在講述 https://github.com/jrburke/almond

相關文章
相關標籤/搜索