CommonJS是用在服务器端的,同步的,如:nodeJS;
AMD、CMD是用在浏览器端的,异步的,如:requireJS和seaJS;
CommonJS
CommonJs 是服务器端模块的规范,Node.js采用了这个规范。
CommonJS规范规定,一个单独的文件就是一个模块。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。
特点:
- 所有代码都运行在模块作用域,不会污染全局作用域。
- 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。如果想让模块再次运行就必须清除缓存。
- 模块加载的顺序是按照其在代码中出现的顺序。
基本语法
- 引入模块:require(‘模块名’),如果是第三方模块,直接传入模块名就可以;如果是自定义模块,需要传入模块文件路径。
- 暴露模块:module.exports = value 或 exports.xxx = value。
CommonJS规范规定,每个模块内部,module变量代表当前模块。这个变量其实是一个对象,它的exports属性(即module.exports)是对外的接口。加载某个模块,其实是加载该模块的module.exports属性。
require方法读取一个文件后并执行该JavaScript文件,最后返回文件内部的exports对象。
如下:exe.js
//私有变量
var test = 666666;
//共有方法
function People(name,age){
this.name = name;
this.age = age;
this.like = function(){
//do something...
}
}
//exports对象上的方法和变量是共有的
var Andi = new People('Andi',23);
exports.Andi = Andi;
require方法默认读取js文件,所以可以省略js后缀
var test = require('./exe').Andi;
test.like();
CommonJS加载模块是同步的,所以只有加载完成后才能执行后面的操作。像nodeJS主要用于服务器编程,加载的模块文件一般都已经存在本地硬盘,所以加载起来比较块,不用考虑异步加载方式,因此CommonJS规范比较使用。
如果是浏览器环境,要从服务器加载模块,这就必须采用异步模式了,所以就有了AMD、CMD解决方案。
模块加载机制
CommonJS模块的加载机制是,输入的是被输出的值的拷贝。也就是说,一旦输出一个值,模块内部的变化就不会影响到模块输出的值。
//lib.js
var counter = 2;
function incCounter(){
counter++;
}
module.exports = {
counter:counter,
incCounter:incCounter
}
lib.js文件输出内部变量counter和改写该变量的内部方法incCounter。
//main.js
var lib = require('./lib');
console.log(lib.counter);//3
lib.incCounter();
console.log(lib.counter);//3
上面代码说明,lib输出以后,lib.js模块内部的变化不会影响到lib了,因为lib.counter是一个原始类型的值,会被缓存。除非写成一个函数,才能得到内部变动后的值。
AMD(Asynchromous Module Definition)
AMD是requireJS在推广过程中对模块定义的规范花产出。
AMD是异步加载模块,它的模块支持对象、函数、构造器、字符串、JSON等类型的模块。
AMD规范用define方法定义模块
通过数组引入依赖,回调函数通过形参传入依赖
define(['module1','module2','module3'],function(module1,module2){
function foo(){
//do something
module1.test();
}
return {foo:foo};
})
AMD规范允许输出模块兼容CommonJS规范,这时define方法如下:
define(funtion(require,exports,module){
var module1 = require('./module1');
module1.test();
exports.fun1 = function(){
//something
}
})
CMD(Common Module Definition)
CMD是SeaJS在推广过程中对模块化定义的规范化产出,和AMD很相似,尽量保持简单。
CMD规范专门用于浏览器端,模块的加载是异步的,模块使用时才会加载执行。
CMD和AMD的主要区别有一下几点:
1、对于模块的依赖AMD的提前执行,CMD是延迟执行。requireJS从2.0开始,也开始可以延迟执行了。
2、CMD推崇依赖就近,AMD推崇依赖前置。
3、AMD的API默认是一个当多个用,CMD严格区分推崇职责单一。AMD里require分全局的和局部的。CMD里面没有全局的 require,提供 seajs.use()来实现模块系统的加载启动。CMD里每个API都简单纯粹。
AMD写法:
define(['./a','/.b'],function(a,b){
//依赖一开始就写好
a.test();
b.test();
})
CMD写法:
define(function(require,exports,module){
//依赖就近写法
var a = require('./a');
a.test();
if(states){
var b = require('./b');
b.test();
}
//通过 exports 就可以向外提供接口
exports.each = function(arr){
//do something
}
})
CMD规范基本用法
- 定义暴露的模块:
//定义没有依赖的模块
define(function(require,exports,module){
exports.xxx = value;
module.exports = value;
})
//定义有依赖的模块
define(function(require,exports,module){
//引入依赖模块(同步)
var module1 = require('./module1');
//引入依赖模块(异步)
require.async('./module2',function(ms){
})
//暴露模块
exports.xxx = value;
})
- 引入使用的模块:
在CMD规范中,如果我们想使用某个模块,只需要通过seajs.use()方法就可以。
//使用sai模块
seajs.use('sai',function(Sai){
//使用模块sai中的init方法
Sai.init();
})
ES6模块化
ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。
ES6模块化语法
- import 命令用于输入其他模块提供的功能
- export 命令用于规定模块的对外接口
/** 定义模块math.js **/
var num = 10;
var add = function(a,b){
return a + b;
}
export {num, add};
/** 引入模块 **/
import {num, add} from './math';
console.log(add(10,num));//20
上面的例子使用 import 命令的时候,用户需要知道所加载变量名和函数名,否则无法加载。
为了给用户提供方便,让他们不用阅读文档就能加载模块,就用到了export default 命令,微模块指定默认输出。
//export-module.js
export default function(){
console.log('输出了');
}
//import-module.js
import custom from './export-module';
custom();//输出了
使用export default 指定默认输出,当其他模块加载该模块时,import 命令可以为该模块指定任意命名。
ES6模块与CommonJS模块的差异
它们有两个重大的差异
- CommonJS模块输出的是一个值的拷贝,ES6模块输出的是值的引用。
- CommonJS模块是运行时加载,ES6模块是编译时输出接口。