天天看點

requireJs 子產品化程式設計立即執行函數寫法

立即執行函數寫法

使用”立即執行函數”(Immediately-Invoked Function Expression,IIFE),可以達到不暴露私有成員的目的。

  var module1 = (function(){
    var _count = ;
    var m1 = function(){
      //...
    };
    var m2 = function(){
      //...
    };
    return {
      m1 : m1,
      m2 : m2
    };
  })();
           

使用上面的寫法,外部代碼無法讀取内部的_count變量。

放大模式

如果一個子產品很大,必須分成幾個部分,或者一個子產品需要繼承另一個子產品,這時就有必要采用”放大模式”(augmentation)。

  var module1 = (function (mod){
    mod.m3 = function () {
      //...
    };
    return mod;
  })(module1);
           

上面的代碼為module1子產品添加了一個新方法m3(),然後傳回新的module1子產品。

寬放大模式(Loose augmentation)

在浏覽器環境中,子產品的各個部分通常都是從網上擷取的,有時無法知道哪個部分會先加載。如果采用上一節的寫法,第一個執行的部分有可能加載一個不存在空對象,這時就要采用”寬放大模式”。

  var module1 = ( function (mod){
    //...
    return mod;
  })(window.module1 || {});
           

與”放大模式”相比,"寬放大模式"就是”立即執行函數”的參數可以是空對象。

輸入全局變量

獨立性是子產品的重要特點,子產品内部最好不與程式的其他部分直接互動。

為了在子產品内部調用全局變量,必須顯式地将其他變量輸入子產品。

  var module1 = (function ($, YAHOO) {
    //...
  })(jQuery, YAHOO);
           

上面的module1子產品需要使用jQuery庫和YUI庫,就把這兩個庫(其實是兩個子產品)當作參數輸入module1。這樣做除了保證子產品的獨立性,還使得子產品之間的依賴關系變得明顯。

JQuery1.5中最末一句

window.jQuery = window.$ = jQuery;
           

AMD

AMD是”Asynchronous Module Definition”的縮寫,意思就是”異步子產品定義”。它采用異步方式加載子產品,子產品的加載不影響它後面語句的運作。所有依賴這個子產品的語句,都定義在一個回調函數中,等到加載完成之後,這個回調函數才會運作。

子產品的加載

使用require.config()方法,我們可以對子產品的加載行為進行自定義。require.config()就寫在主子產品(main.js)的頭部。參數就是一個對象,這個對象的paths屬性指定各個子產品的加載路徑。

 require.config({
    paths: {
      "jquery": "jquery.min",
      "underscore": "underscore.min",
      "backbone": "backbone.min"
    }
  });
           

上面的代碼給出了三個子產品的檔案名,路徑預設與main.js在同一個目錄(js子目錄)。如果這些子產品在其他目錄,比如js/lib目錄,則有兩種寫法。一種是逐一指定路徑。

require.config({
    paths: {
      "jquery": "lib/jquery.min",
      "underscore": "lib/underscore.min",
      "backbone": "lib/backbone.min"
    }
  });
           

另一種則是直接改變基目錄(baseUrl)。

 

 require.config({
    baseUrl: "js/lib",
    paths: {
      "jquery": "jquery.min",
      "underscore": "underscore.min",
      "backbone": "backbone.min"
    }
  });
           

如果某個子產品在另一台主機上,也可以直接指定它的網址,比如:

  

require.config({
    paths: {
      "jquery": "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min"
    }
  });
           

require.js要求,每個子產品是一個單獨的js檔案。這樣的話,如果加載多個子產品,就會發出多次HTTP請求,會影響網頁的加載速度。是以,require.js提供了一個優化工具,當子產品部署完畢以後,可以用這個工具将多個子產品合并在一個檔案中,減少HTTP請求數。

AMD子產品的寫法

require.js加載的子產品,采用AMD規範。也就是說,子產品必須按照AMD的規定來寫。

具體來說,就是子產品必須采用特定的define()函數來定義。如果一個子產品不依賴其他子產品,那麼可以直接定義在define()函數之中。

假定現在有一個math.js檔案,它定義了一個math子產品。那麼,math.js就要這樣寫:

  // math.js
  define(function (){
    var add = function (x,y){
      return x+y;
    };
    return {
      add: add
    };
  });
           

如果這個子產品還依賴其他子產品,那麼define()函數的第一個參數,必須是一個數組,指明該子產品的依賴性。

  define(['myLib'], function(myLib){
    function foo(){
      myLib.doSomething();
    }
    return {
      foo : foo
    };
  });
           

加載方法如下:

  // main.js
  require(['math'], function (math){
    alert(math.add(,));
  });
           

主子產品的寫法

require(['jquery', 'underscore', 'backbone'], function ($, _, Backbone){
    // some code here
  });
           

加載非規範的子產品

理論上,require.js加載的子產品,必須是按照AMD規範、用define()函數定義的子產品。但是實際上,雖然已經有一部分流行的函數庫(比如jQuery)符合AMD規範,更多的庫并不符合。那麼,require.js是否能夠加載非規範的子產品呢?

回答是可以的。

這樣的子產品在用require()加載之前,要先用require.config()方法,定義它們的一些特征。

舉例來說,underscore和backbone這兩個庫,都沒有采用AMD規範編寫。如果要加載它們的話,必須先定義它們的特征。

  require.config({
    shim: {

      'underscore':{
        exports: '_'
      },
      'backbone': {
        deps: ['underscore', 'jquery'],
        exports: 'Backbone'
      }
    }
  });
           

require.config()接受一個配置對象,這個對象除了有前面說過的paths屬性之外,還有一個shim屬性,專門用來配置不相容的子產品。具體來說,每個子產品要定義(1)exports值(輸出的變量名),表明這個子產品外部調用時的名稱;(2)deps數組,表明該子產品的依賴性。

比如,jQuery的插件可以這樣定義:

  shim: {
    'jquery.scroll': {
      deps: ['jquery'],
      exports: 'jQuery.fn.scroll'
    }
  }
           

require.js插件

require.js還提供一系列插件,實作一些特定的功能。

domready插件,可以讓回調函數在頁面DOM結構加載完成後再運作。

  require(['domready!'], function (doc){
    // called once the DOM is ready
  });
           

text和image插件,則是允許require.js加載文本和圖檔檔案。

  define([
    'text!review.txt',
    'image!cat.jpg'
    ],

    function(review,cat){
      console.log(review);
      document.body.appendChild(cat);
    }
  );
           

繼續閱讀