天天看點

js子產品化開發學習

子產品化開發了解

Javascript子產品化程式設計,主要用于實作開發者隻需要實作核心的業務邏輯,其他都可以加載别人已經寫好的子產品。

子產品就是實作特定功能的一組方法。有了子產品,我們就可以友善的使用别人的代碼,需要什麼功能,就加載相應的子產品。

一個子產品其實就是一個實作特定功能的檔案,這個檔案中封裝了一些函數和變量。每個子產品都提供了對外通路的接口,使得使用者可以友善的調用子產品裡面封裝的方法。

有了子產品,我們就可以更友善地使用别人的代碼,需要什麼函數來實作指定功能,就加載相應的子產品就可以了。子產品開發需要大家按照同樣的方式來編寫子產品,否則就都亂套了。

目前,通用的js子產品規範有:AMD(

Asynchronous Module Definition

異步子產品定義)、

CMD

(Common Module Definition) 通用子產品定義和commonJS。其中AMD和CMD是浏覽器端子產品化開發的規範,commonJS是伺服器端的規範。

AMD規範

該規範通過define方法來定義子產品,require方法來調用子產品,實作代碼的子產品加載。子產品将被異步加載,子產品加載不影響後面語句的運作。所有依賴這個子產品的語句,都定義在一個回調函數中,等到加載完成之後,這個回調函數才會運作。

AMD

RequireJS

在推廣過程中對子產品定義的規範化的産出。

AMD就隻有一個接口:define(id?,dependencies?,factory);

它要在聲明子產品的時候制定所有的依賴(dep),并且還要當做形參傳到factory中,像這樣:

define(['dep1','dep2'],function(dep1,dep2){...});
           

要是沒什麼依賴,就定義簡單的子產品,下面這樣就可以啦:

define(function(){
    var exports = {};
    exports.method = function(){...};
    return exports;
});
           

RequireJS

學習

<!DOCTYPE html>
<html >
    <head>
        <meta charset="utf-8">
        <title>require</title>
    </head>
    <body>
    <script type="text/javascript" src="js/require.js" data-main="js/main.js"></script>
    </body>
</html>
           

主子產品main.js,是整個頁面的入口代碼

//main.js是主子產品,主子產品需要依賴其他子產品來進行工作
//alert("加載成功");
require(['math','sorts'],function(math,sorts){
    console.log(math.add(10,12));
    console.log(math.chu(24,12));
    console.log([1,5,4,3,2].sort(sorts.compare));
});
           

其他子產品

sorts.js

define(function(){
    var compare=function(num1,num2){
        if(num1<num2){
            return -1;
        }else if(num1>num2){
            return 1;
        }else{
            return 0;
        }
    };
    return {
        compare:compare
    };
});
           
//math.js
define(function(){
    var add=function(x,y){
        return x+y;
    };
    var chu=function(x,y){
        return x/y;
    };
    return {
        add:add,
        chu:chu
    };
});
           

require()函數接受兩個參數。第一個參數是一個數組,表示主子產品所依賴的子產品;第二個參數是一個回調函數,目前面指定的子產品都加載成功後,它将被調用。加載的子產品會以參數形式傳入該函數,進而在回調函數内部就可以使用這些子產品。主子產品的代碼就寫在回調函數中

require()異步加載所需子產品,浏覽器不會失去響應;它指定的回調函數,隻有前面的子產品都加載成功後,才會運作,解決了依賴性的問題。

require.config配置

require.config是用來配置子產品加載位置,簡單點說就是給子產品起一個更短更好記的名字,比如将百度的jquery庫位址标記為jquery,這樣在require時隻需要寫["jquery"]就可以加載該js,本地的js我們也可以這樣配置:

require.config({
    paths : {
        "jquery" : ["http://libs.baidu.com/jquery/2.0.3/jquery"],
        "a" : "js/a"   
    }
})
require(["jquery","a"],function($){
    $(function(){
        alert("load finished");  
    })
})
           

通過paths的配置會使我們的子產品名字更精煉,paths還有一個重要的功能,就是可以配置多個路徑,如果遠端cdn庫沒有加載成功,可以加載本地的庫,如:

require.config({
    paths : {
        "jquery" : ["http://libs.baidu.com/jquery/2.0.3/jquery", "js/jquery"],
        "a" : "js/a"   
    }
})
require(["jquery","a"],function($){
    $(function(){
        alert("load finished");  
    })
})
           

這樣配置後,當百度的jquery沒有加載成功後,會加載本地js目錄下的jquery.在使用requirejs時,加載子產品時不用寫.js字尾的,當然也是不能寫字尾

全局配置

上面的例子中重複出現了require.config配置,如果每個頁面中都加入配置,必然顯得十分不雅,requirejs提供了一種叫"主資料"的功能,我們首先建立一個main.js:

require.config({
    paths : {
        "jquery" : ["http://libs.baidu.com/jquery/2.0.3/jquery", "js/jquery"],
        "a" : "js/a"   
    }
})
           

然後再頁面中使用下面的方式來使用requirejs:

<script data-main="js/main" src="js/require.js"></script>
           

加載requirejs腳本的script标簽加入了data-main屬性,這個屬性指定的js将在加載完reuqire.js後處理,我們把require.config的配置加入到data-main後,就可以使每一個頁面都使用這個配置,然後頁面中就可以直接使用require來加載所有的短子產品名

data-main還有一個重要的功能,當script标簽指定data-main屬性時,require會預設的将data-main指定的js為根路徑。如上面的data-main="js/main"設定後,我們在使用require(['jquery'])後(不配置jquery的paths),require會自動加載js/jquery.js這個檔案,而不是jquery.js。由于require.js預設的檔案字尾名是js,是以可以把main.js簡寫成main。

加載非規範的子產品

這樣的子產品在用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插件

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

類似的插件還有json和mdown,用于加載json檔案和markdown檔案。

CMD規範

CMD

(Common Module Definition) 通用子產品定義。該規範明确了子產品的基本書寫格式和基本互動規則。該規範是在國内發展出來的。AMD是依賴關系前置,CMD是按需加載。

AMD 是 RequireJS 在推廣過程中對子產品定義的規範化産出。

CMD是SeaJS在推廣過程中對子產品定義的規範化産出

對于依賴的子產品,

AMD

是提前執行,

CMD

是延遲執行。

CMD:延遲執行(運作按需加載,根據順序執行)
           

<span style="font-weight: normal;">// 所有子產品都通過 define 來定義
define(function(require, exports, module) {

  // 通過 require 引入依賴
  var $ = require('jquery');
  var Spinning = require('./spinning');

  // 通過 exports 對外提供接口
  exports.doSomething = ...

  // 或者通過 module.exports 提供整個接口
  module.exports = ...

});</span>
           

SeaJS

學習

<!DOCTYPE html>
<html >
    <head>
        <meta charset="utf-8">
        <title>seajs</title>
    </head>
    <body>
    <script type="text/javascript" src="sea.js"></script>
    <script type="text/javascript">
        seajs.use('dialog',function(Dialog){
            //alert(Util.add(1,2));
            //console.log([1,5,4,3,2].sort(Util.sorts));//[1, 2, 3, 4, 5]
            var res=Dialog.sum([1,5,4,3,2]);
            console.log(res);//15
        });
    </script>
    </body>
</html>
           

util,js

define(function(require,exports){
    exports.add=function(x,y){
        return x+y;
    };
    exports.sorts=function(num1,num2){
        if(num1>num2){
            return 1;
        }else if(num1<num2){
            return -1;
        }else{
            return 0;
        }
    };
});
           

dialog.js

define(function(require,exports){
    var util=require('./util.js');
    exports.sum=function(arr){
        var sum=0;
        console.log(arr.sort(util.sorts));
        for(var i=0;i<arr.length;i++){
            sum+=arr[i];
        }
        return sum;
    };
});
           

SeaJS對子產品的态度是懶執行, 而RequireJS對子產品的态度是預執行

SeaJS隻會在真正需要使用(依賴)子產品時才執行該子產品

SeaJS是異步加載子產品的沒錯, 但執行子產品的順序也是嚴格按照子產品在代碼中出現(require)的順序.

而RequireJS會先盡早地執行(依賴)子產品, 相當于所有的require都被提前了, 而且子產品執行的順序也不一定100%就是先mod1再mod2

是以你看到執行順序和你預想的完全不一樣!

CommonJS規範

CommonJS

是伺服器端子產品的規範,

Node.js

采用了這個規範。Node.JS首先采用了js子產品化的概念。

根據

CommonJS

規範,一個單獨的檔案就是一個子產品。每一個子產品都是一個單獨的作用域,也就是說,在該子產品内部定義的變量,無法被其他子產品讀取,除非定義為

global

對象的屬性。

加載子產品使用

require

方法,該方法讀取一個檔案并執行,最後傳回檔案内部的

module.exports

對象。

待續。。。。

繼續閱讀