1、简介

随着前端业务复杂度的增加,模块化成为一个大的趋势。而在ES6还未被浏览器所支持的情况下,commonjs作为ES6中标准模块加载方案,在客服端中的支持情况并不好,现在在客服端中有2中模块化的解决方案,CMD和AMD,他们的代表分别为seajs和requirejs。这篇文章主要介绍我对commonjs、AMD以及CMD的理解。

2、commonJS

commonjs的目标是制定一个js模块化的标准,它的目标制定一个可以同时在客服端和服务端运行的模块。这些模块拥有自己独立的作用域,也可以向顶层曝露出自己的api也就是module.exports。在ES6中common被制定为标准。但是在ES6还未被浏览器完美支持的情况下,commonjs规范之能在服务端发挥它的作用。比如在nodejs和webpack等中。而我们服务端的异步加载模块主要有2种加载方案,CMD和AMD,这两种规范的典型是seajs和rjs(requirejs)。这两种方案虽然都是加载模块的解决方案,但是还是有一些的差别。

我们先来了解下commonjs规范。

此规范指出了如何编写可以在同类模块系统中所共用的模块,这类模块系统可以同时在客户端和服务端,以安全的或者不安全的方式已经被实现了或者通过语法扩展可以被未来的系统所支持。这些模块需要提供顶级作用域的私有性,并提供从其他模块导入单例对象到自身并且可以导出自身API的能力。含蓄的说,这个规范定义了如果一个模块系统要支持共用模块,那么它需要提供的最少的功能特性。

模块上下文

  1. 在一个模块中,存在一个自由的变量"require",它是一个函数。

    1. 这个"require"函数接收一个模块标识符。
    2. "require"返回外部模块所输出的API。
    3. 如果出现依赖闭环(dependency cycle),那么外部模块在被它的传递依赖(transitive dependencies)所require的时候可能并没有执行完成;在这种情况下,"require"返回的对象必须至少包含此外部模块在调用require函数(会进入当前模块执行环境)之前就已经准备完毕的输出。
    4. 如果请求的模块不能返回,那么"require"必须抛出一个错误。
  2. 在一个模块中,会存在一个名为"exports"的自由变量,它是一个对象,模块可以在执行的时候把自身的API加入到其中。
  3. 模块必须使用"exports"对象来做为输出的唯一表示。

如果不理解请移步CommonJS Modules/1.0 规范 & AMD 规范中文版 这里面会有具体的例子。

从模块的上下文可以可以看出,这只是制定了一种模块的方式,在模块内部用require函数去获取我们所需要的API,用exports抛出当前模块的API。这其实也就是我们node的模块方式,没错,node就是commonjs的实践者,commonjs只是一种规范,而nodejs就是实践这个规范。同样的,ES6中,也执行了commonjs规范,但是介于我们浏览器还不能完美的支持ES6,所以我们也不能用这种很cool的方式。(注解:当然babel是一个完美的工具,把ES6转成目前支持的es5。但是最终在浏览器中执行的代码还是es5。所以我们直接编写的ES6代码,只能通过webpack等打包工具,把模块之间的依赖通过打包工具来实现。)

既然无法在浏览器中使用ES6,那么我们如和在浏览器中去执行模块依赖加载的呢?这里有2中方案去实现浏览器下模块和依赖加载,CMD和AMD,这两种方案的典型是seajs和requirejs。下面我们先分析AMD

3、AMD规范

AMD是为了弥补commonjs规范在浏览器中目前无法支持ES6的一种解决方案。异步模块定义规范(AMD)制定了定义模块的规则,这样模块和模块的依赖可以被异步加载。这和浏览器的异步加载模块的环境刚好适应(浏览器同步加载模块会导致性能、可用性、调试和跨域访问等问题)。

这个规范只定义define函数。

define(id?,dependencies?,factory);

其中id和dependencies不是必须的。

模块的格式:

模块名用来唯一标识定义中模块,它们同样在依赖数组中使用。AMD的模块名规范是CommonJS模块名规范的超集。引用如下:

  • 模块名是由一个或多个单词以正斜杠为分隔符拼接成的字符串
  • 单词须为驼峰形式,或者".",".."
  • 模块名不允许文件扩展名的形式,如".js"
  • 模块名可以为 "相对的" 或 "顶级的"。如果首字符为"."或".."则为"相对的"模块名
  • 顶级的模块名从根命名空间的概念模块解析
  • 相对的模块名从 "require" 书写和调用的模块解析

上文引用的CommonJS模块id属性常被用于JavaScript模块。

相对模块名解析示例:

  • 如果模块 "a/b/c" 请求 "../d", 则解析为"a/d"
  • 如果模块 "a/b/c" 请求 "./e", 则解析为"a/b/e"

如果AMD的实现支持加载器插件(Loader-Plugins),则"!"符号用于分隔加载器插件模块名和插件资源名。由于插件资源名可以非常自由地命名,大多数字符都允许在插件资源名使用。

define.amd (Object)用来标识有amd模块加载器的存在

例子:  

// 创建一个名为"alpha"的模块,使用了require,exports,和名为"beta"的模块:define("alpha", ["require", "exports", "beta"], function (require, exports, beta) {exports.verb = function() {return beta.verb();//Or:return require("beta").verb();}});//一个返回对象的匿名模块:define(["alpha"], function (alpha) {return {verb: function(){return alpha.verb() + 2;}};});//一个没有依赖性的模块可以直接定义对象:
   define({add: function(x, y){return x + y;}});// 一个使用了简单CommonJS转换的模块定义:define(function (require, exports, module) {var a = require('a'),b = require('b');exports.action = function () {};});

4、AMD规范的实践者requirejs

requirejs是AMD规范的实践者,RequireJS 是一个JavaScript模块加载器。它非常适合在浏览器中使用,但它也可以用在其他脚本环境, 就像 Rhino and Node. 使用RequireJS加载模块化脚本将提高代码的加载速度和质量。

在使用requirejs时,可以查看官方文档

requirejs中文文档

requirejs英文文档

requirejs非常简单,我们只需要定义在页面加载的时候,引入requirejs并且,把mainjs指定在data-main中,在mainjs中引入我们的requirejs.config和我们需要用到的页面js,requirejs会根据我们的模块去加载相应的依赖,然后执行代码。

// 页面引入
<script data-main="main" src="./amdjs/require.js"></script>// 模块,这里使用AMD定义模块的方式,例如,定义一个模块module1
define('module1', ['zepto'], function($) {console.log('this is module1')
})//mainjs内容
require.config({baseUrl: 'amdjs/modules',paths: {main: 'amdjs/main'zepto: 'http://zeptojs.com/zepto.min'},shim: {},waitSeconds: 15
});
// 你的模块
requirejs(['module1'],function($) {console.log('load success!')
})

config文件里面有许多参数,这里我把常用的解释下,具体的请查看requirejs文档。

baseUrl:所有模块的查找根路径。

paths :path映射那些不直接放置于baseUrl下的模块名。设置path时起始位置是相对于baseUrl的,除非该path设置以"/"开头或含有URL协议(如http:)。

shim: 为那些没有使用define()来声明依赖关系、设置模块的"浏览器全局变量注入"型脚本做依赖和导出配置。shim配置仅设置了代码的依赖关系,想要实际加载shim指定的或涉及的模块,仍然需要一个常规的require/define调用。设置shim本身不会触发代码的加载。

deps: 指定要加载的一个依赖数组。当将require设置为一个config object在加载require.js之前使用时很有用。一旦require.js被定义,这些依赖就已加载。使用deps就像调用require([]),但它在loader处理配置完毕之后就立即生效。它并不阻塞其他的require()调用,它仅是指定某些模块作为config块的一部分而异步加载的手段而已。

5、CMD规范

cmd是commonjs另外的一种模块加载方案,这个规范本身偏向于commonjs的规范。他以一个文件就是一个模块和ES6中标准的commonjs规范类似。

它定义了以个define(factory)函数。define 接受 factory 参数,factory 可以是一个函数,也可以是一个对象或字符串。

如果factory为函数时,它有三个参数:require,exports,module。

define.cmd是一个cmd模块加载器的标识

require方法:同步加载模块

define(function(require, exports) {// 获取模块 a 的接口var a = require('./a');// 调用模块 a 的方法
  a.doSomething();});

require.async:用来在模块的内部异步加载模块,并且完成后执行指定回掉。

define(function(require, exports, module) {// 异步加载一个模块,在加载完成时,执行回调require.async('./b', function(b) {b.doSomething();});// 异步加载多个模块,在加载完成时,执行回调require.async(['./c', './d'], function(c, d) {c.doSomething();d.doSomething();});});

require.resolve:使用模块系统内部的路径解析机制来解析并返回模块路径。该函数不会加载模块,只返回解析后的绝对路径。

define(function(require, exports) {console.log(require.resolve('./b'));// ==> http://example.com/path/to/b.js

});

exports:是一个对象,用来向外提供模块接口。

define(function(require) {// 通过 return 直接提供接口return {foo: 'bar',doSomething: function() {}};});

module.exports:模块暴露的出口

define(function(require, exports, module) {// 正确写法module.exports = {foo: 'bar',doSomething: function() {}};});

6、CMD规范的实践者seaJS

seajs和requirejs的加载方式类似,在页面引入seajs文件后,加载seajs.config,并且之后加载mainjs。详细信息请查看

seajs文档。

seajsAPI

加载seajs

// 引入seajs
<script src="../sea-modules/seajs/seajs/2.2.0/sea.js"></script>// 配置文件
<script>// Set configuration
  seajs.config({base: "../sea-modules/",alias: {"jquery": "jquery/jquery/1.10.1/jquery.js"}});
// 入口// For developmentif (location.href.indexOf("?dev") > 0) {seajs.use("../static/hello/src/main");}// For productionelse {seajs.use("examples/hello/1.0.0/main");}
</script>

API:

seajs.config: 配置信息

seajs.config({// 设置路径,方便跨目录调用
  paths: {'arale': 'https://a.alipayobjects.com/arale','jquery': 'https://a.alipayobjects.com/jquery'},// 设置别名,方便调用
  alias: {'class': 'arale/class/1.0.0/class','jquery': 'jquery/jquery/1.10.1/jquery'}});

更多的详细配置选项请参考页面:seajs config详细

seajs.use:页面加载一个或者多个模块

// 加载一个模块
seajs.use('./a');// 加载一个模块,在加载完成时,执行回调
seajs.use('./a', function(a) {a.doSomething();
});// 加载多个模块,在加载完成时,执行回调
seajs.use(['./a', './b'], function(a, b) {a.doSomething();b.doSomething();
});

这里值得一提的是seajs.use的用法,它替代了AMD中require(【】,factory)的用法。

define:用来定义模块。

define(function(require, exports, module) {// 模块代码

});

require: 用来获取指定模块的接口。

define(function(require) {// 获取模块 a 的接口var a = require('./a');// 调用模块 a 的方法
  a.doSomething();
});

require.async:用来在模块内部异步加载一个或多个模块。

define(function(require) {// 异步加载一个模块,在加载完成时,执行回调require.async('./b', function(b) {b.doSomething();});// 异步加载多个模块,在加载完成时,执行回调require.async(['./c', './d'], function(c, d) {c.doSomething();d.doSomething();});});

exports: 用来在模块内部对外提供接口

define(function(require, exports) {// 对外提供 foo 属性exports.foo = 'bar';// 对外提供 doSomething 方法exports.doSomething = function() {};});

module.exports:与 exports 类似,用来在模块内部对外提供接口

define(function(require, exports, module) {// 对外提供接口module.exports = {name: 'a',doSomething: function() {};};});

seajs API具体的用法,请参考seajs API

7、CMD和AMD之间的差异

1)AMD(异步加载模块),CMD(通用模块),AMD是需要通过异步加载的形式把依赖加载进来,然而CMD在require依赖的时候,可以通过同步的形式(require),也可以通过异步的形式(require.async)。当然AMD也可以通过特殊的写法支持CMD,但是不推崇。

2)CMD 推崇依赖就近,AMD 推崇依赖前置。在AMD中,我们需要把依赖前置在依赖数组中。而在cmd中,我们只需要在使用这个模块前,把依赖的模块require进来。

3)设计理念不一样,在 SeaJS 里,API 的设计理念是:

  • 保持简单,职责单一。
  • 遵守规范,但不拘泥。
  • 适度灵活

requirejs中,require的用法多样,比如:

require('a')  -- gets exports of module a

require(['a']) -- fetch module a according to module name scheme

require(['a.js'])  -- fetch a.js directly relative to current page

require({...})  -- set loader config

4)聚焦点有差异

seajs专注于浏览器环境下的模块加载,而requirejs集成了在node环境以及Rhino 环境下的代码,这导致requirejs比seajs更大。

参考文献:

commonjs规范

CommonJS Modules/1.0 规范 & AMD 规范中文版

AMD规范

requirejs中文文档

requirejs英文文档

CMD规范

seajs文档

seajs和requirejs的异同

注:本人见识短浅,有不够准确的地方万望指正。拜谢!

转载于:https://www.cnblogs.com/omelette/p/6652472.html

关于commonjs,AMD,CMD之间的异同相关推荐

  1. commonjs是什么_JavaScript模块化标准CommonJS/AMD/CMD/UMD/ES6Module的区别

    JS-模块化进程 随着js技术的不断发展,途中会遇到各种问题,比如模块化. 那什么是模块化呢,他们的目的是什么? 定义:如何把一段代码封装成一个有用的单元,以及如何注册此模块的能力.输出的值依赖引用: ...

  2. CommonJS,AMD,CMD,ES6,require 和 import 详解

    CommonJS,AMD,CMD,ES6 commonJS用同步的方式加载模块.在服务端,模块文件都存在本地磁盘,读取非常快,所以这样做不会有问题.但是在浏览器端,限于网络原因,更合理的方案是使用异步 ...

  3. JS JavaScript模块化(ES Module/CommonJS/AMD/CMD)

    前言 前端开发中,起初只要在script标签中嵌入几十上百行代码就能实现一些基本的交互效果,后来js得到重视,应用也广泛起来了, jQuery,Ajax,Node.Js,MVC,MVVM等的助力也使得 ...

  4. 关于 CommonJS AMD CMD UMD 规范的差异总结

    根据CommonJS规范,一个单独的文件就是一个模块.每一个模块都是一个单独的作用域,也就是说,在一个文件定义的变量(还包括函数和类),都是私有的,对其他文件是不可见的. // foo.js var ...

  5. 关于JavaScript的模块(CommonJS, AMD, CMD, ES6模块)的理解

    Javascript模块化就是解决将代码进行分隔,作用域隔离,模块之间的依赖管理等多个方面问题. 这样的优点不言而喻:1.可维护性2.命名空间私有化,可以避免污染全局环境3.代码重用,通过模块可以方便 ...

  6. CommonJS/AMD/CMD/UMD

    为什么会有这几种模式? 起源:Javascript模块化 模块化就是把复杂问题分解成不同模块,这样可维护性高,从而达到高复用,低耦合. 1.Commonjs CommonJS是服务器端模块的规范,No ...

  7. 模块格式:CommonJS AMD CMD UMD ES6

    皆为前端工程化中定义模块(module)的规则,如:模块标识,模块依赖,模块实现,模块对外接口,模块加载: 实现时有:异步,同步,依赖前置,依赖就近,预执行,懒执行,软依赖,硬依赖,模块对象支持类型等 ...

  8. CommonJs, AMD/RequireJs,CMD/seajs

    JavaSript模块化 在了解AMD,CMD规范前,还是需要先来简单地了解下什么是模块化,模块化开发? 模块化是指在解决某一个复杂问题或者一系列的杂糅问题时,依照一种分类的思维把问题进行系统性的分解 ...

  9. AMD, CMD, CommonJS和UMD

    AMD, CMD, CommonJS和UMD 今天由于项目中引入的echarts的文件太大,requirejs经常加载超时,不得不分开来加载echarts的各个图表.但是使用echarts自带的在线构 ...

最新文章

  1. 通过网页查看服务器算法,服务器使用国密(SM2/SM3/SM4)证书,通过浏览器访问
  2. 关于OnTouchListener的深入理解
  3. 2018高中计算机会考知识点,2018高中物理会考知识点总结
  4. vue 实现 router store 文件自动加载
  5. ubuntu18安装sublime
  6. 学术词汇 | Ablation Test or Ablation Experiment
  7. C语言宏定义中#define中的井号#的使用
  8. 【Node学习】—运行node服务demo
  9. 用于创建此对象的程序是excel_一起学Excel专业开发22:使用类模块创建对象1
  10. jqueryUI日期控件和时间控件
  11. 2021年游戏开发中的10大编程语言:C++、Java、C#......
  12. 中国独角兽上市潮,爱奇艺优信小米值得投资吗?
  13. vs2010最佳配色选择_2010年代35部最佳电影
  14. word07 去掉标题前的黑点
  15. 利用GDAL根据栅格影像DN值实现颜色渲染
  16. 黑科技丨电脑必备的chrome插件(一)
  17. ITIL 4 升级大揭秘,你关心的问题都全了!
  18. NameNode概述
  19. 反向迭代器和正向迭代器
  20. JVM知识整理----基础和垃圾处理

热门文章

  1. synchronized 的超多干货!
  2. 为什么说Netty是性能之王,因为它用了 Reactor 模型啊
  3. 懂点 Nginx 反向代理与负载均衡,是面试加分项没有之一
  4. SpringBoot第十八篇: 定时任务(Scheduling Tasks)
  5. 鱼佬:从数据竞赛到工作!
  6. 特征工程(五)length
  7. 遇事不决,量子力学:谷歌量子计算模拟化学反应登上Science封面
  8. 知乎热议:周志华弟子 旷视南京负责人跳槽高校
  9. 既然很多工作 35 岁就会被裁员,那么深耕一个领域的意义是什么?
  10. 计算机、数学、运筹学等领域的32个重要算法