AMD 和 CMD

一、前言

这是我们经常写在 HTML 中的语句,引用了很多 js 文件,但是看不清他们彼此之间的依赖关系:

  <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 文件还能不能正常工作。很难看出之间的依赖关系。

js 文件之间有变量的污染,为了避免麻烦进行无脑合并 js 文件时,会出现一个问题。它们之间有变量名冲突,所以的为每个 JS 文件都加上 IIFE(immediately invoking function express)的外壳:

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

这种无脑合并的隐患很大,比如一个sum函数其他的js中要用,此时是没有定义域的。(中介者模式可以解决IIFE之间的调用window.a)

(function(){
    //这个函数写在IIFE中,
    function sum(){

    }
})();
(function(){
    //这里面没有sum的定义
    alert(sum(3,4));
})();
(function(){
    //也定义了一个sum,很乱
    function sum(){

    }
})();

所以这种合并,需要的不是IIFE,而是一个命名空间。

二、AMD规范 - require.js

Asynchronous Module Defination 异步模块定义。代表库就是 require.js。
require.js的用法 - 阮一峰的网络日志 :http://www.ruanyifeng.com/blog/2012/11/require_js.html

2.1 简单demo

使用 require.js 的第一步,是先去官方网站 [下载] (http://requirejs.org/docs/download.html)最新版本。
<script src="js/require.js" data-main="js/main"></script>
在main.js中写一条语句:main可直接执行
alert("你好");
data-main属性的作用是,静态化main.js,由于require.js默认的文件后缀名是js,所以可以把main.js简写成main。

2.2 函数暴露和模块的引用

文件夹结构:

┣  js
┃  ┣  equire.js
┃  ┣  main.js
┃  ┣  circle.js
┣  index.html
  • 函数暴露
    我们使用define()来包裹一个函数,define不是ECMAScript的语法,而是require.js的语法。
    函数里面可以写任何语句,需要暴露的东西,写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
})

我们发现,依赖注入的语法是:

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

});

注意:
1)依赖的文件名必须有引号,注入的时候没有引号的;
2)注入的时候的名字可以任取,所以require.js是通过依赖注入的顺序来产生对应关系的
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 设置暴露口

AMD规范最强大的事情,就是在于一个普通的JS文件如果不符合AMD规范,可以设置暴露口。

什么叫做符合AMD规范:如果一个库在创建的时候有这么一条语句(伪代码):

define(function(){
    return {}
})

此时就说明对require.js兼容了,此时叫做符合AMD规范。
比如我们引入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 中定义这层关系:

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规范?(本质)

首先我们要知道依赖注入的时候,依赖的模块们(比如下面的4个依赖)彼此之间没有加载顺序之分的。只有一个亘古不变的真理:当他们都加载完毕之后,执行回调函数。

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("我最先执行");
2.7任何普通模块也可以有依赖注入。
define(["fang","circle"],function(fang,circle){
    return {
        fang,
        circle
    }
});

这是才通过主文件调用的话,多此一举,特别类似中介者模式;比如console.log(c.f.fang(9,9));

总结,到底为什么叫做AMD规范:
1)AMD规范使用依赖注入的形式,所有的依赖项是同时加载的,没有回来的先后之分,都回来了才执行回调函数。也就是说,回调函数是AMD规范的特征。
2)require()里面的语句是异步语句,把依赖项都加载完毕之后才执行回调函数,此时就是“异步”的由来。
3)AMD规范中,如果两个模块之间有“插件”的关系,彼此依赖,可以用shim中定义这个关系。

AMD的现状:很惨。
 只有require.js对AMD进行了实现。
 Angular1使用了AMD规范,而Angular2放弃了AMD规范,转入了CMD阵营,Angular4、5都是CMD的。

三、CMD规范

Common Module Definition,通用模块定义。
它的实现:common.js、sea.js(淘宝玉伯)、node.js。

我们看sea.js的实现。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总结:
1)nodejs是遵循CMD规范的,可以裸奔CMD规范。
2)所有的模块都要用define(function(require,exports,module){})包裹,称为“标准壳”;
3)暴露有两种途径exports.** = ** ; module.exports = ** 。
4)引用的时候用require()引用,require谁就执行谁,会死等这个文件加载完毕,没有回调函数。
5)CMD规范中没有node_modules这个神奇的文件夹的概念,是nodejs自己添加的特性。

最后说一嘴:AMD、CMD规范和业务一点关系没有,就是纯粹的文件组织的形式。网页DOM开发是不会用AMD、CMD规范的,现在AMD、CMD学习的意义就是服务于Angular、React、Vue的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,258评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,335评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,225评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,126评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,140评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,098评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,018评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,857评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,298评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,518评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,400评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,993评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,638评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,661评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容