前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >AMD && CMD

AMD && CMD

作者头像
超然
发布2018-08-03 16:37:14
1.8K0
发布2018-08-03 16:37:14
举报
文章被收录于专栏:超然的博客超然的博客

前言

JavaScript初衷:实现简单的页面交互逻辑,寥寥数语即可;

随着web2.0时代的到来,Ajax技术得到广泛应用,jQuery等前端库层出不穷,前端代码日益膨胀

问题:

这时候JavaScript作为嵌入式的脚本语言的定位动摇了,JavaScript却没有为组织代码提供任何明显帮助,甚至没有类的概念,JavaScript极其简单的代码组织规范不足以驾驭如此庞大规模的代码

一、模块

模块化:是一种处理复杂系统分解为代码结构更合理,可维护性更高的可管理的模块的方式。

在理想状态下我们只需要完成自己部分的核心业务逻辑代码,其他方面的依赖可以通过直接加载被人已经写好模块进行使用即可。

无模块

代码语言:javascript
复制
<script src="jquery.js"></script>   
<script src="jquery_scroller.js"></script>   
<script src="main.js"></script>   
<script src="other1.js"></script>   
<script src="other2.js"></script>   
<script src="other3.js"></script>

优点:

相比于使用一个js文件,这种多个js文件实现最简单的模块化的思想是进步的。 

缺点:

污染全局作用域。 因为每一个模块都是暴露在全局的,简单的使用,会导致全局变量命名冲突,当然,我们也可以使用命名空间的方式来解决。 对于大型项目,各种js很多,开发人员必须手动解决模块和代码库的依赖关系,后期维护成本较高。 依赖关系不明显,不利于维护。 比如main.js需要使用jquery,但是,从上面的文件中,我们是看不出来的,如果jquery忘记了,那么就会报错。

二、CommonJS

CommonJs 是服务器端模块的规范,Node.js采用了这个规范。

根据CommonJS规范,一个单独的文件就是一个模块。加载模块使用require方法,该方法读取一个文件并执行,最后返回文件内部的exports对象。

代码语言:javascript
复制
var math = require('math');
math.add(2, 3);

第二行math.add(2, 3),在第一行require('math')之后运行,因此必须等math.js加载完成。也就是说,如果加载时间很长,整个应用就会停在那里等。您会注意到 require 是同步的。

CommonJS 加载模块是同步的,所以只有加载完成才能执行后面的操作。像Node.js主要用于服务器的编程,加载的模块文件一般都已经存在本地硬盘,所以加载起来比较快,不用考虑异步加载的方式,所以CommonJS规范比较适用。但如果是浏览器环境,要从服务器加载模块,这是就必须采用异步模式。所以就有了 AMD  CMD 解决方案。

三、AMD

AMD 即 Asynchronous Module Definition,中文名是异步模块定义的意思。它是一个在浏览器端模块化开发的规范

AMD也采用require()语句加载模块,但是不同于CommonJS,它要求两个参数:

代码语言:javascript
复制
require([module], callback);

第一个参数[module],是一个数组,里面的成员就是要加载的模块;第二个参数callback,则是加载成功之后的回调函数。如果将前面的代码改写成AMD形式,就是下面这样:

代码语言:javascript
复制
require(['math'], function (math) {
  math.add(2, 3);
});

math.add()与math模块加载不是同步的,浏览器不会发生假死。所以很显然,AMD比较适合浏览器环境。目前,主要有两个Javascript库实现了AMD规范:require.jscurl.js

与 RequireJS

AMD 是 RequireJS 在推广过程中对模块定义的规范化产出

AMD异步加载模块。它的模块支持对象 函数 构造器 字符串 JSON等各种类型的模块。

代码语言:javascript
复制
//通过数组引入依赖 ,回调函数通过形参传入依赖
define(['Module1', ‘Module2’], function (Module1, Module2) {

    function foo () {
        /// someing
        Module1.test();
    }

    return {foo: foo}
});
  • 第一个参数 id 为字符串类型,表示了模块标识,为可选参数。若不存在则模块标识应该默认定义为在加载器中被请求脚本的标识。如果存在,那么模块标识必须为顶层的或者一个绝对的标识。
  • 第二个参数,dependencies ,是一个当前模块依赖的,已被模块定义的模块标识的数组字面量。
  • 第三个参数,factory,是一个需要进行实例化的函数或者一个对象。

    创建模块标识为 Module1 的模块,依赖于 require, export,和标识为 beta 的模块  

AMD规范允许输出模块兼容CommonJS规范,这时define方法如下:

代码语言:javascript
复制
define(function (require, exports, module) {
    
    var reqModule = require("./someModule");
    requModule.test();
    
    exports.asplode = function () {
        //someing
    }
});

优点:

适合在浏览器环境中异步加载模块。可以并行加载多个模块。 缺点:

提高了开发成本,并且不能按需加载,而是必须提前加载所有的依赖。

四、CMD

CMD是SeaJS 在推广过程中对模块定义的规范化产出

CMD和AMD的区别有以下几点:

1.对于依赖的模块AMD是提前执行,CMD是延迟执行。不过RequireJS从2.0开始,也改成可以延迟执行(根据写法不同,处理方式不通过)。

2.AMD推崇依赖前置(在定义模块的时候就要声明其依赖的模块),CMD推崇依赖就近(只有在用到某个模块的时候再去require——按需加载)。

代码语言:javascript
复制
//AMD
define(['./a','./b'], function (a, b) {

    //依赖一开始就写好
    a.test();
    b.test();
});

//CMD
define(function (requie, exports, module) {
    
    //依赖可以就近书写
    var a = require('./a');
    a.test();
    
    ...
    //软依赖
    if (status) {
    
        var b = requie('./b');
        b.test();
    }
});

3.AMD的api默认是一个当多个用,CMD严格的区分推崇职责单一。例如:AMD里require分全局的和局部的。CMD里面没有全局的 require,提供 seajs.use()来实现模块系统的加载启动。CMD里每个API都简单纯粹。

AMD 是 RequireJS 在推广过程中对模块定义的规范化产出,CMD是SeaJS 在推广过程中被广泛认知。RequireJs出自dojo加载器的作者James Burke,SeaJs出自国内前端大师玉伯。二者的区别,玉伯在12年如是说

RequireJS 和 SeaJS 都是很不错的模块加载器,两者区别如下:

代码语言:javascript
复制
1. 两者定位有差异。RequireJS 想成为浏览器端的模块加载器,同时也想成为 Rhino / Node 等环境的模块加载器。SeaJS 则专注于 Web 浏览器端,同时通过 Node 扩展的方式可以很方便跑在 Node 服务器端

2. 两者遵循的标准有差异。RequireJS 遵循的是 AMD(异步模块定义)规范,SeaJS 遵循的是 CMD (通用模块定义)规范。规范的不同,导致了两者API 的不同。SeaJS 更简洁优雅,更贴近 CommonJS Modules/1.1 和 Node Modules 规范。

3. 两者社区理念有差异。RequireJS 在尝试让第三方类库修改自身来支持 RequireJS,目前只有少数社区采纳。SeaJS 不强推,而采用自主封装的方式来“海纳百川”,目前已有较成熟的封装策略。

4. 两者代码质量有差异。RequireJS 是没有明显的 bug,SeaJS 是明显没有 bug。

5. 两者对调试等的支持有差异。SeaJS 通过插件,可以实现 Fiddler 中自动映射的功能,还可以实现自动 combo 等功能,非常方便便捷。RequireJS无这方面的支持。

6. 两者的插件机制有差异。RequireJS 采取的是在源码中预留接口的形式,源码中留有为插件而写的代码。SeaJS 采取的插件机制则与 Node 的方式一致开放自身,让插件开发者可直接访问或修改,从而非常灵活,可以实现各种类型的插件。

优点: 同样实现了浏览器端的模块化加载。 可以按需加载,依赖就近。

缺点: 依赖SPM打包,模块的加载逻辑偏重。

 五、 ES6 模块

CommonJS

  • 对于基本数据类型,属于复制。即会被模块缓存。同时,在另一个模块可以对该模块输出的变量重新赋值。
  • 对于复杂数据类型,属于浅拷贝。由于两个模块引用的对象指向同一个内存空间,因此对该模块的值做修改时会影响另一个模块。
  • 当使用require命令加载某个模块时,就会运行整个模块的代码。
  • 当使用require命令加载同一个模块时,不会再执行该模块,而是取到缓存之中的值。也就是说,CommonJS模块无论加载多少次,都只会在第一次加载时运行一次,以后再加载,就返回第一次运行的结果,除非手动清除系统缓存。
  • 循环加载时,属于加载时执行。即脚本代码在require的时候,就会全部执行。一旦出现某个模块被"循环加载",就只输出已经执行的部分,还未执行的部分不会输出。

ES6模块

  • ES6模块中的值属于【动态只读引用】。
  • 对于只读来说,即不允许修改引入变量的值,import的变量是只读的,不论是基本数据类型还是复杂数据类型。当模块遇到import命令时,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。
  • 对于动态来说,原始值发生变化,import加载的值也会发生变化。不论是基本数据类型还是复杂数据类型。
  • 循环加载时,ES6模块是动态引用。只要两个模块之间存在某个引用,代码就能够执行。

参考:

关于 CommonJS AMD CMD UMD

JavaSript模块规范 - AMD规范与CMD规范介绍

JavaScript模块化 --- Commonjs、AMD、CMD、ES6 modules

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017-04-11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 一、模块
  • 二、CommonJS
  • 三、AMD
  • 四、CMD
  •  五、 ES6 模块
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档