前端模塊化:AMD和CMD,後端模塊化:commonJS,ES6模塊化

如果你對模塊化已經瞭解,可以直接從第三點開始閱讀。

一、模塊化的概念:

在進行項目分析或者編碼時,先把項目進行拆分,拆分成很多的類,對象,很多的函數等等。能夠提高代碼的複用性。這些被拆分出來的類型,對象,函數就是模塊。就像一輛汽車是由很多的零部件組成,每個零部件就是一個小模塊,而由很多零件組成的發動機之於一輛汽車可以認爲是一個大模塊;或者說,一臺計算機,由主板,cpu,內存,硬盤,顯示器,鍵盤等大模塊組成。而一個內存或者cpu又分別由很多的小模塊組成。以模塊爲單位管理代碼,會更加獨立,調試方便,維護也很方便。

 

二、模塊化經歷的階段:

    1. 函數封裝

這種做法的缺點很明顯:污染了全局變量,無法保證不與其他模塊發生變量名衝突,而且模塊成員之間沒什麼關係。不同js文件裏有相同的全局變量,如果被引入到同一個html文件中,全局變量就會互相影響。

     2. 對象

這樣避免了變量污染,只要保證模塊名唯一即可,同時同一模塊內的成員也有了關係,看似不錯的解決方案,但是也有缺陷,外部(對象之外)可以隨意修改內部成員(屬性)

對象的屬性就是對象中每個方法的全局變量。

var p = {

    id:"007",

    name :"芙蓉",

    age:25,    

    eat:function(str){

        alert(this.name+"在喫"+str);

    },

    work:function(str){

        alert(this.name+“在幹"+str);

    }           

}

//存在問題:

//如:對於p對象的年齡,有效取值應該是在0-150之間的整數。

//  而以下代碼的執行,

p.age = 151;

都會使得項目內部出現了不合法的數據。這是程序的健壯性不好。   

所以,對於對象的成員變量(屬性),應該不能被外部訪問纔對。

  3.  立即執行函數

可以通過立即執行函數,來達到隱藏細節的目的


var p = (function (){

    var name="芙蓉";

    var age=25;

    function eat(str){

        alert(this.name+"在喫"+str);

    }

    function setAge(age){

        if(age<0 || age>150){

            alert("親,年齡超出有效值(0-150)的範圍");

            return;

        }

        age = age;

    }

    function getAge(){

        return age;

    }

    return {

        eat:eat,

        setAge:setAge,

        getAge:getAge

    }                   

})();  

var p = (function (){

    var name="芙蓉";

    var age=25;

    function eat(str){

        alert(this.name+"在喫"+str);

    }

    function setAge(age){

        if(age<0 || age>150){

            alert("親,年齡超出有效值(0-150)的範圍");

            return;

        }

        age = age;

    }

    function getAge(){

        return age;

    }

    return {

        eat:eat,

        setAge:setAge,

        getAge:getAge

    }                   

})();  

    4. 模塊化的解決方案。

  •  以上模塊化存在的問題:

不管是以上哪種方式(函數,對象,立即執行函數),都存在同樣的問題:

1)、html不但要引入自己需要的js文件,還需要引入js文件需要的js文件。如:a.html需要使用 b.js的代碼,而由於b.js中使用了c.js裏的代碼。所以,在a.html中必須引入b.js和c.js。這是JavaScript語言先天性的缺陷----js文件沒法引入js文件。其它編程語言(java,c#,c/c++)就不存在這個問題。

2)、引入js文件的順序問題以及異步加載問題。

 

  • 爲此出現了一些解決方案:

 1)、前端模塊化

    在前端裏出現了第三方的解決方案 AMD和CMD

 2)、後端模塊化

    在後端裏(nodeJS)出現了commonJS規範。

 

三、模塊化的概念

1、導出:導出就是對外開放的,可以導出變量,常量,函數,對象等等。使用export關鍵字。放在export關鍵字後面的(即對外導出的)變量,常量,函數和對象,在其它js文件中可以使用,否則,其它js文件中是不能使用的。即只能內部使用的。

               模塊化的這種思路,可以隱藏細節,開放接口

2、導入:導入就是把其它js文件引入到當前js文件裏。

 

四、前端模塊化:AMD和CMD

   1、ES6之前,原生不支持模塊化,所以出現了第三方的解決方案,分別是AMD和CMD

       AMD: Asynchronous Module Definition,中文名是異步模塊定義的意思。它是一個在瀏覽器端模塊化開發的規範,由於不是JavaScript原生支持,使用AMD規範進行頁面開發需要用到對應的庫函數(RequireJS庫),實際上AMD 是 RequireJS 在推廣過程中對模塊定義的規範化的產出

      CMD:  ”Common Module Definition”,稱爲 通用模塊加載規範 。一般也是用在瀏覽器端。瀏覽器端異步加載庫 Sea.js 實現的就是CMD規範。

  2、AMD之requireJS

    

 1)、引入第三方庫requireJS

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

 2)、html文件中引入主模塊

主模塊就是html文件所(直接)依賴的模塊(文件),使用script標籤的data-main屬性。假如html需要依賴的文件是main.js , 則代碼如下:

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

如果main.js需要引入其它js文件,那怎麼辦?我們就需要知道如何定義模塊,如何引入模塊。

即html文件的最終代碼是:<

!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <meta http-equiv="X-UA-Compatible" content="ie=edge">

    <title>Document</title>

</head>

<body>

    <input id="btn01" type="button" value="測試" />

</body>

</html>

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

  3)、主模塊引入其它模塊(就是導入)main.js代碼

假如,main.js引入的是person.js。那麼 main.js 裏的寫法如下:


//require :引入其它模塊 

//require函數的第一個參數:是引入的模塊名,是數組

//requrie函數的第二個參數,是回調函數,回調函數的參數,

//對應到模塊裏的return。 即 p 對應着js/person.js裏的return。

require(["js/person.js"],function(p){

    document.getElementById("btn01").onclick = function(){

       p.eat("方便麪");

       p.work();

    }

});

 4)、定義模塊(person.js)

導出用return,引入別的模塊,在define的第一個參數寫就行。

假如,main.js引入的是person.js。則person.js裏的代碼寫法如下:


// 這表示當前模塊不依賴其它模塊

define([],function(){

    var name ="張三瘋";

    var age = 12;

    

    return {

        eat:function(str){

            alert(name+"在喫"+str);

        },

        work:function(){

            alert(name+"在努力地工作……");

        }

    }

});

  5)、如果person.js模塊還需要其它模塊(如:dog.js)的話,怎麼辦?

5.1)、修改一下,person.js的代碼。


//define :定義模塊

//define 函數的第一個參數:是引入(依賴)的模塊名,是數組

//define 函數的第二個參數,是回調函數,回調函數的參數,

// 對應到模塊裏的return。 即 d 對應着js/dog.js裏的return。



define(["js/dog.js"],function(d){

    var name ="張三瘋";

    var age = 12;

    

    return {

        eat:function(str){

            alert(name+"在喫"+str);

        },

        work:function(){

            alert(name+"在努力地工作……");

        },

        lookDoor:function(){

            d.lookDoor();

        }

    }

});

5.2)、增加一個dog.js的代碼。


//這表示當前模塊不依賴其它模塊

define(function(){

    var name ="大黃";

    

    return {

        lookDoor:function(){

            alert(name+"在看門");

        }

    }

});

3、CMD之seaJS

   1)、引入seaJS(在html裏):

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <meta http-equiv="X-UA-Compatible" content="ie=edge">

    <title>Document</title>

</head>

<body>

    <input id="btn01" type="button" value="測試" />

</body>

</html>

<script src="js/sea.js"></script>

<script src="js/main_sea.js"></script>

  2)、主模塊的引入:

<script src="js/main_sea.js"></script>

 3)、主模塊引入其它模塊(main_sea.js代碼),

假如main_sea.js引入person_sea.js

//在主模塊裏,用seajs引入其它模塊,用seajs.use函數。

seajs.use(["person_sea"],function(p){

    document.getElementById("btn01").onclick = function(){

        p.eat("方便麪");

        p.work();

        p.lookDoor();

     }

});

 4)、模塊的定義(person_sea.js):

//seajs中定義模塊,用define函數。

//引入其它模塊:不在第一個參數,使用回調函數裏的requrie;

//導出本模塊的屬性或者方法:使用回調函數裏的exports

define(function(require,exports) {

    var name ="張三瘋";

    var age = 12;

    //對外導出(開發)eat函數

    exports.eat = function(str){

        alert(name+"在喫"+str);

    };

    

    //對外導出(開發)work函數

    exports.work = function(){

        alert(name+"在努力地工作……");

    };



    //此時需要dog模塊,所以,用require引入。

    let d = require("dog_sea");

    exports.lookDoor=function(){

        d.lookDoor();

    }

});

dog_sea.js的代碼。

define(function(require,exports) {

    var name ="大黃";

    exports.lookDoor=function(){

        alert(name+"在看門");

    }

});

4、總結一下,使用模塊化的步驟:

1、定義模塊

 1.1)、引入別的模塊

 1.2)、對外導出本模塊的哪些屬性和方法

2)、主模塊引入其它模塊

3)、html引入主模塊

 

5、對比AMD和CMD的區別:

5.1)、定義模塊

 都使用的都是define函數

 5.1.1)、引入別的模塊

 AMD:

在define函數的第一個參數

 define([依賴的模塊列表],function(引入模塊的變量名列表){});

 CMD:

在define函數的第二個參數(回調函數)的require參數。

 define(function(require,exports){

    let d = require("dog");//引入dog模塊

 });

 5.1.2)、對外導出本模塊的哪些屬性和方法

AMD:

用return

CMD:

在define函數的第二個參數(回調函數)的exports參數。

5.2)、主模塊引入其它模塊

AMD:

require函數

     require([引入的其它模塊列表],function(其它模塊對應的變量名列表){

     });

CMD:

 seajs.use([引入的其它模塊列表],function(其它模塊對應的變量名列表){

});

5.3)、html引入主模塊(假如主模塊是main.js)

AMD:  

     1)、script標籤的 data-main

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

      2)、也可以使用script標籤引入主模塊。

      即:

      <script src="js/require.js" ></script>

      <script src="js/main.js" ></script>

CMD:  

    script標籤引入主模塊

    <script src="js/sea.js" ></script>

    <script src="js/main.js" ></script>

 

5.4)、模塊路徑問題:

AMD:

基準路徑是html的文件夾

     define(["js/dog.js"],function(d){})

CMD:

就是js文件的文件夾,

      let d = require("dog_sea");

5.5)、依賴方面:

5.5.1). 對於依賴的模塊,AMD 是提前執行,CMD 是延遲執行。不過 RequireJS 從 2.0 開始,也改成可以延遲執行(根據寫法不同,處理方式不同)。CMD 推崇 as lazy as possible(儘可能的懶惰)

5.5.2). CMD 推崇依賴就近,AMD 推崇依賴前置。

5.5.3)如下代碼示例:

 a模塊依賴b模塊和c模塊的代碼格式

  1. 、AMD

a.js的代碼:

define(['./b', './c'], function(b, c) { // 依賴必須一開始就寫好
             b.doSomething()


         c.doSomething()

    })

  1. 、CMD的代碼:

a.js

define(function(require,exports,module){

var  b = require(“b");

………………

var  c = require(“c");

………………

});

四、後端模塊化:commonJS

 

commonJS是後端模塊化的解決方案,在nodeJS中有使用(nodeJS是js做後端的技術,相當於php,jsp,aspx)。

 

咱們曾經寫的webpack就是用的commonJS規範。

 

1、導出:

module.expors={

     Name:‘張三瘋’,

     Eat:function(){

 

     }

}

2、導入

    Require();

 

五、ES6的模塊化:

 1、原生支持模塊化了

ES6中新增的模塊化,即從ES6開始,原生js支持模塊化了,不需要第三方的模塊化庫,現在很多瀏覽器也支持ES6了。

  2、模塊化的兩個概念

1)、導出(export關鍵字):導出就是對外開放的,可以導出變量,常量,函數,對象等等。使用export關鍵字。放在export關鍵字後面的(即對外導出的)變量,常量,函數和對象,在其它js文件中可以使用,否則,其它js文件中是不能使用的。即只能內部使用的。

在用export導出時,可以導出多個

如:person.js文件(模塊)裏,如下:

//導出字符串

export var str = "hello";

//導出函數

export var fun = function(){

    alert("我是函數");

}

//導出對象

export const p =  {

    "id":"007",

    "name":"張三瘋",

    "eat":function(){

        alert("喫");

        ff();

    }

}

//此函數沒有使用export關鍵字導出,所以,只能在當前js文件內部使用

function ff(){

    alert("我只能在當前js被使用");

}

2)、導入(import):導入就是把其它js文件引入到當前js文件裏。使用關鍵字import。

 

在使用import導入(export導出的)時,要使用花括號,

如:import {str,fun,p} from './person.js';

 

在index.js文件中,引入模塊person.js;

//導入時,需要使用{},這是解構賦值。

import {str,fun,p} from './person.js';

window.onload = function(){

    document.getElementById("btn01").onclick = function(){

        console.log(str);

        fun();

        console.log(p.id);

        p.eat();

    }

}

3)、在html文件中引入index.js(注意: type="module")
注意:js中使用了模塊化的關鍵字import,在引入時,script標籤的type屬性的值必須寫成module。即:<script type="module" src="js/index.js"></script>

 

<body>

    <input id="btn01" type="button" value="測試" />

</body>

<script type="module" src="js/index.js"></script>

注意:測試以上代碼時,google瀏覽器要求放在服務器上進行 ,否則,就會有跨域問題。

4)、export defaultexport 有什麼區別:

  1. 、export與export default均可用於導出常量、函數、文件、模塊等
  2. 、在一個文件或模塊中,export可以有多個,export default僅有一個,而且export default在導出是不需要變量名,相當於匿名的。
  3. 、通過export方式導出,在導入時要加{ },export default則不需要。

代碼示例:

模塊定義:dog.js

export default {

    "name":"大黃",

    "eat":function(){

        alert("喫");

    }

}

導入模塊:

import d from './dog.js';

window.onload = function(){

    document.getElementById("btn01").onclick = function(){

        console.log(d);

        d.eat();

    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章