在学习js和使用js的过程中一定会使用到模块化开发的思想。而提到模块化就一定会提到CommonJS、RequireJS、AMD、CMD等名词。下面我将根据自己查阅的资料梳理以下这几个名词背后的关联。
首先上个图,直观感受下这个几个名词的关系:
commonjs使用在服务器端的,模块的加载方式是同步的,如nodejs
amd,cmd是用在浏览器端,这两种规范规定的模块的加载方式是异步的,它们的对应的模块加载器分别是requirejs和seajs。其中,amd先提出,cnd是根据commonjs和amd基础上提出的。
CommonJS
CommonJS是服务器端模块的规范,Node.js采用了这个规范。
根据CommonJS规范,一个单独的文件就是一个模块。模块加载使用require方法,该方法读取一个文件并执行,最后返回文件内部的exports对象。
例如:
//foobar.js
//私有变量
var test=123;
//公有方法
function foobar(){
this.foo=function(){
// do something…
}
this.bar=function(){
//do something
}
}
//exports对象上的方法和变量是公有的
var foobar=new foobar();
exports.foobar=foobar;
//require方法默认读取js文件,所以可以省略js后缀
var test=require(“./boobar”).foobar;
test.bar();
CommonJS加载模块是同步的,所以只有加载完成才能执行后面的操作。像Node.js主要用于服务器的编程,加载的模块文件一般都已经存在本地硬盘,所以加载起来比较快,不用考虑异步加载的方式,所以CommonJS规范比较适用。但如果是浏览器环境,要从服务器加载模块,这时就需要采用异步模式。所以就有了AMD和CMD解决方案。
AMD(Asynchromous Module Definition)
AMD是RequireJS在推广过程中对模块定义的规范化产出。
AMD是异步加载模块。它的模块支持对象 函数 构造器 JSON等各种类型的模块。
适用AMD规范适用define方法定义模块。
//通过数组引入依赖,回调函数通过形参传入依赖
define([‘someModule1’,’someModule2’],function(someModule1,someModule2){
function foo(){
//something
someModule1.test();
}
return {foo:foo};
});
AMD规范允许输出模块兼容CommonJS规范,这时define方法如下:
define(function(require,exports,module){
var reqModule=require(“./someModule”);
reqMoudle.test();
exports.asplode=function(){
//something
}
})
CMD
CMD是SeaJS在推广过程中对模块定义的规范化产出
CMD 和AMD的区别有以下几点:
1.对于依赖的模块AMD是提前执行,CMD是延迟执行。不过RequireJS从2.0开始,也改成可以延迟执行(根据写法不同,处理方式不通过)
2.CMD推崇依赖就近。AMD推崇依赖前置
//AMD
define([‘./a’,’./b’],function(a,b){
//依赖一开始就写好
a.test();
b.test();
})
//CMD
define(function(require,exports,module){
//依赖可以就近书写
var a=require(‘./a’);
a.test();
…
//软依赖
if(status){
var b=require(‘./b’)
b.test();
}
})
虽然AMD也支持CMD写法,但依赖前置是官方文档的默认模块定义写法。
3.AMD的api默认是一个当多个用,CMD严格的区分推崇职责单一。例如:AMD里require分全局的和局部的。CMD里面没有全局的require,提供seajs.use()来实现模块系统的加载启动。CMD里每个API都简单纯粹。
Seajs和RequireJS的主要区别在此有解释。
续:RequireJS和SeaJS与CommonJS的比较(严格意义上前两者与后者不该放在一起比较,因为前两者是规范的具体实现,而后者是一种规范)
RequireJS实现了AMD的API
CommonJS是使用exports对象来定义模块的一种方法,它定义了模块的内容。简单地实现一个CommonJS定义就像下面这样
//someModule.js
exports.doSomething=function(){return “foo”};
//otherModule.js
var someModule=reuqire(“someModule”);
exports.doSomethingElse=function(){
return someModule.doSomething()+”bar”;
}
基本上CommonJS明确了你需要一个reuqire函数来获取依赖,exports变量来输入模块的内容和一些用来获取依赖的模块标识符。CommonJS有多种实现,比如Node.js
因为CommonJS设计的时候没有考虑浏览器,所以他不适合浏览器环境所以它不适合浏览器环境(我其实对这个不明确,但是这种说法到处都有,比如RequireJS官网)。所以我们得做一些工作来实现异步加载。相反,RequireJS实现了AMD,它被设计用来适应浏览器环境。表面上看来,AMD开始是CommonJS输出格式化的副厂品,而且最终进化出了自己的API。在AMD中出现的新东西是define函数,它允许模块加载依赖之前声明它的依赖。例如定义可能就像下面这样:
define(‘module/id/string’,[‘module’,’dependency’,’array’],function(module,factory function){
return ModuleCotents;
});
因此CommonJS和AMD是Javascript模块定义API的不同的实现,但是他们有相同的根源。AMD更适合浏览器,因为它支持异步加载模块依赖。RequireJS是AMD的一个实现,而尽量保留了CommonJS的精神(主要是模块标识符上)。更让人混乱的是,ReuqireJS是实现AMD的同事,还提供了一个CommonJS包裹,这样CommonJS模块可以几乎直接被RequireJS引入。define(function(reuqire,exports,module){
var someModule=require(‘someModule’);
exports.doSomethingElse=function(){
return someModule.doSomething()+”bar”;
} )