AMD 和 CMD

一、前言

這是我們經(jīng)常寫在 HTML 中的語句,引用了很多 js 文件,但是看不清他們彼此之間的依賴關(guān)系:

  <script src="1.js"></script>
  <script src="2.js"></script>
  <script src="3.js"></script>
  <script src="4.js"></script>
  <script src="5.js"></script>
  <script src="6.js"></script>

刪除 1.js、2.js 之后,其他的 js 文件還能不能正常工作。很難看出之間的依賴關(guān)系。

js 文件之間有變量的污染,為了避免麻煩進行無腦合并 js 文件時,會出現(xiàn)一個問題。它們之間有變量名沖突,所以的為每個 JS 文件都加上 IIFE(immediately invoking function express)的外殼:

(function(){
    
})();
(function(){
    
})();
(function(){
    
})();
(function(){
    
})();
(function(){
    
})();

這種無腦合并的隱患很大,比如一個sum函數(shù)其他的js中要用,此時是沒有定義域的。(中介者模式可以解決IIFE之間的調(diào)用window.a)

(function(){
    //這個函數(shù)寫在IIFE中,
    function sum(){

    }
})();
(function(){
    //這里面沒有sum的定義
    alert(sum(3,4));
})();
(function(){
    //也定義了一個sum,很亂
    function sum(){

    }
})();

所以這種合并,需要的不是IIFE,而是一個命名空間。

二、AMD規(guī)范 - require.js

Asynchronous Module Defination 異步模塊定義。代表庫就是 require.js。
require.js的用法 - 阮一峰的網(wǎng)絡(luò)日志 :http://www.ruanyifeng.com/blog/2012/11/require_js.html

2.1 簡單demo

使用 require.js 的第一步,是先去官方網(wǎng)站 [下載] (http://requirejs.org/docs/download.html)最新版本。
<script src="js/require.js" data-main="js/main"></script>
在main.js中寫一條語句:main可直接執(zhí)行
alert("你好");
data-main屬性的作用是,靜態(tài)化main.js,由于require.js默認的文件后綴名是js,所以可以把main.js簡寫成main。

2.2 函數(shù)暴露和模塊的引用

文件夾結(jié)構(gòu):

┣  js
┃  ┣  equire.js
┃  ┣  main.js
┃  ┣  circle.js
┣  index.html
  • 函數(shù)暴露
    我們使用define()來包裹一個函數(shù),define不是ECMAScript的語法,而是require.js的語法。
    函數(shù)里面可以寫任何語句,需要暴露的東西,寫return來暴露。
define(function(){
    function mianji(a,b){
        return a * b;
    }
    return {
        mianji //k、v相同,省略v
    }
});
  • 模塊的引用
    主文件需要使用“依賴注入”(dependencies injection)的方式引入這個模塊。
require(['circle'],function(c){
    alert(c.mianji(10,10)); //100
})

我們發(fā)現(xiàn),依賴注入的語法是:

require(["模塊1","模塊2","模塊3"],function(模塊1,模塊2,模塊3){

});

注意:
1)依賴的文件名必須有引號,注入的時候沒有引號的;
2)注入的時候的名字可以任取,所以require.js是通過依賴注入的順序來產(chǎn)生對應(yīng)關(guān)系的
3)依賴的時候,沒有拓展名,模塊的默認名字就是文件名。

2.3 別名

模塊的名字默認是文件名。

require(['yuan','fang'],function(y,f){
    alert(y.mianji(10));
    alert(f.mianji(10,20));
});

如果我們想把依賴的名字改掉,此時可以使用require對象的config方法來配置paths項:

require.config({
  paths: {
    "circle" : "yuan"  //沒有.js擴展名,后面的是原文件名,前面的是別名
  }
});
require(['circle','fang'],function(y,f){
    alert(y.mianji(10));
    alert(f.mianji(10,20));
});
2.4 設(shè)置暴露口

AMD規(guī)范最強大的事情,就是在于一個普通的JS文件如果不符合AMD規(guī)范,可以設(shè)置暴露口。

什么叫做符合AMD規(guī)范:如果一個庫在創(chuàng)建的時候有這么一條語句(偽代碼):

define(function(){
    return {}
})

此時就說明對require.js兼容了,此時叫做符合AMD規(guī)范。
比如我們引入jquery庫:

require.config({
  paths: {
    "circle" : "yuan",
        "jq" : "lib/jquery.min"
  }
});

require(['circle','fang','jq'],function(y,f,$){
    alert(y.mianji(10));
    alert(f.mianji(10,20));
    $("#box").css("background-color" , "red");
});
----
main.js:9 Uncaught TypeError: $ is not a function
    at main.js:9
    at Object.execCb (require.js:5)
    at e.check (require.js:5)
    at e.<anonymous> (require.js:5)
    at require.js:5
    at require.js:5
    at each (require.js:5)
    at emit (require.js:5)
    at e.check (require.js:5)
    at enable (require.js:5)
2.5 引入有依賴的依賴的庫

比如我們引入 jquery-ui,很明顯 jquery-ui 是 jquery 的插件。此時我們說 jquery-ui依 賴jquery。
此時我們要在 shim 中定義這層關(guān)系:

require.config({
  paths: {
    "circle" : "yuan",
        "jq" : "lib/jquery.min",
        "jqui" : "lib/jquery-ui.min"
  },
    shim: {
        'jq': {
            exports : '$'
    },
        'jqui': {
      deps: ['jq'] //這是重點
        }
    }
});

require(['circle','fang','jq','jqui'],function(y,f,$,jqui){
    alert(y.mianji(10));
    alert(f.mianji(10,20));
    $("#box").animate({"font-size":400},1000);
    $("#box").draggable();
});

測試如果引入依賴文件可以不寫這步的操作。

2.6為什么叫AMD規(guī)范?(本質(zhì))

首先我們要知道依賴注入的時候,依賴的模塊們(比如下面的4個依賴)彼此之間沒有加載順序之分的。只有一個亙古不變的真理:當(dāng)他們都加載完畢之后,執(zhí)行回調(diào)函數(shù)。

require(['circle','fang','jq','jqui'],function(y,f,$,jqui){
    alert(y.mianji(10));
    alert(f.mianji(10,20));
    $("#box").animate({"font-size":400},1000);
    $("#box").draggable();
});
alert("我最先執(zhí)行");
2.7任何普通模塊也可以有依賴注入。
define(["fang","circle"],function(fang,circle){
    return {
        fang,
        circle
    }
});

這是才通過主文件調(diào)用的話,多此一舉,特別類似中介者模式;比如console.log(c.f.fang(9,9));

總結(jié),到底為什么叫做AMD規(guī)范:
1)AMD規(guī)范使用依賴注入的形式,所有的依賴項是同時加載的,沒有回來的先后之分,都回來了才執(zhí)行回調(diào)函數(shù)。也就是說,回調(diào)函數(shù)是AMD規(guī)范的特征。
2)require()里面的語句是異步語句,把依賴項都加載完畢之后才執(zhí)行回調(diào)函數(shù),此時就是“異步”的由來。
3)AMD規(guī)范中,如果兩個模塊之間有“插件”的關(guān)系,彼此依賴,可以用shim中定義這個關(guān)系。

AMD的現(xiàn)狀:很慘。
? 只有require.js對AMD進行了實現(xiàn)。
? Angular1使用了AMD規(guī)范,而Angular2放棄了AMD規(guī)范,轉(zhuǎn)入了CMD陣營,Angular4、5都是CMD的。

三、CMD規(guī)范

Common Module Definition,通用模塊定義。
它的實現(xiàn):common.js、sea.js(淘寶玉伯)、node.js。

我們看sea.js的實現(xiàn)。https://www.zhangxinxu.com/sp/seajs/

//index.html
<body>
    <script src="js/sea.js"></script>
    <script>
        seajs.use("./js/main.js");
    </script>
</body>
//main.js:
define(function(require,exports,module){
    var yuan = require("./yuan.js");
    var fang = require("./fang.js");
    alert(yuan.mianji(10))
    alert(fang.mianji(10,20))
});

//yuan.js:
define(function(require,exports,module){
    function mianji(r){
        return r * r * 3.14;
    }

    exports.mianji = mianji;
});

CMD總結(jié):
1)nodejs是遵循CMD規(guī)范的,可以裸奔CMD規(guī)范。
2)所有的模塊都要用define(function(require,exports,module){})包裹,稱為“標(biāo)準(zhǔn)殼”;
3)暴露有兩種途徑exports.** = ** ; module.exports = ** 。
4)引用的時候用require()引用,require誰就執(zhí)行誰,會死等這個文件加載完畢,沒有回調(diào)函數(shù)。
5)CMD規(guī)范中沒有node_modules這個神奇的文件夾的概念,是nodejs自己添加的特性。

最后說一嘴:AMD、CMD規(guī)范和業(yè)務(wù)一點關(guān)系沒有,就是純粹的文件組織的形式。網(wǎng)頁DOM開發(fā)是不會用AMD、CMD規(guī)范的,現(xiàn)在AMD、CMD學(xué)習(xí)的意義就是服務(wù)于Angular、React、Vue的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容