0
点赞
收藏
分享

微信扫一扫

AMD 和 CMD

以沫的窝 2021-09-24 阅读 69
一、前言

这是我们经常写在 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的。

举报

相关推荐

0 条评论