一、CommonJS规范(Node.js)
CommonJS用同步的方式加载模块,在服务端,模块文件都存在本地磁盘,读取非常快,所有node.js是CommonJS规范的主要实践者,但在浏览器端受网络影响,更适合用异步加载的方案。
CommonJS规范有四个重要的环境变量为模块化的实现提供支持:module、exports、require、global。在实际使用时,用module.exports定义当前模块对外的输出的接口(不推荐直接用exports),用require加载模块。
// 定义模块math.js
var basicNum = 0;
function add(a, b) {
return a + b;
}
module.exports = {
//在这里写上需要向外暴露的函数、变量
add: add,
basicNum: basicNum
}
// 引用自定义的模块时,参数包含路径,可省略.js
var math = require('./math');
math.add(2, 5);
// 引用核心模块时,不需要带路径
var http = require('http');
http.createService(...).listen(3000);
exports和module.exports的区别
exports = module.exports = {} exports可以看成module.exports的一种简化,本质上输出是一样的,但是对exports或者module.exports本身进行赋值,都会造成其间的连接中断
//在a.js中写入该代码
module.exports.userName = "赵大胖";
exports.age = 21;
exports = {};
exports.height = 169;
//在b.js中引入自定义模块a.js
var result = require("./a.js"); //假定两个文件是在同个目录下的
console.log(result);
//最后的打印结果为{userName:"赵大胖",age:21}
//原因如下:
/*
(1)在第1、2行代码里,exports和module.exports向同一个对象里添加了两个属性,userName和age
(2)在第3、4行代码,exports重新指向了一个新的对象,并给这个对象里添加了一个新的属性height
(3)由于最后返回的是module.exports指向的对象,所以最后返回的是{userName:"赵大胖",age:22}
*/
在node中推荐使用module.exports,因为module.exports才是node自带的,exports是commonJS提供的
二、AMD规范(require.js)
AMD规范采用异步方式加载模块,所有依赖这个模块的语句都定义在一个回调函数中,等到加载完成后,这个回调函数才会运行。AMD是由JavaScript社区提出的专注于支持浏览器端模块化的标准。
使用define定义导出模块:
define('getSum', ['calculator'], function(math) {
return function(a, b) {
console.log('sum' + calculator.add(a, b))
}
})
define接收三个参数,参数说明如下:
- 当前模块的id,相当于模块名
- 当前模块的依赖
- 模块的导出值,可以是函数或者对象,如果是函数导出的就是函数的返回值,如果是对象直接导出对象本身
使用require函数加载模块:
require(['getSum'], function(getSum) {
getSum(2, 3)
})
require接收2个参数,参数说明如下:
- 指定加载的模块
- 当加载完成后执行的回调函数
AMD存在的缺点:
- 与同步加载的模块标准相比语法更加冗长
- 异步加载方式不明显,容易造成回调地狱,在目前的实际应用中逐渐减少
三、CMD规范(sea.js)
CMD与AMD的语法类似,但是CMD通过按需加载的方式,而不是必须在模块开始加载所有的依赖 CMD推崇依赖就近,延迟执行。AMD是预加载,CMD是懒加载
- 优点:同样实现了浏览器端的模块加载,可以按需加载,依赖就近
- 缺点:依赖SPM打包,模块的加载逻辑
/** AMD写法 **/
define(["a", "b", "c", "d", "e", "f"], function(a, b, c, d, e, f) {
// 等于在最前面声明并初始化了要用到的所有模块
a.doSomething();
if (false) {
// 即便没用到某个模块 b,但 b 还是提前执行了
b.doSomething()
}
});
/** CMD写法 **/
define(function(require, exports, module) {
var a = require('./a'); //在需要时申明
a.doSomething();
if (false) {
var b = require('./b');
b.doSomething();
}
});
/** sea.js **/
// 定义模块 math.js
define(function(require, exports, module) {
var $ = require('jquery.js');
var add = function(a,b){
return a+b;
}
exports.add = add;
});
// 加载模块
seajs.use(['math.js'], function(math){
var sum = math.add(1+2);
});
四、ES6模块化
在ES6中使用export导出模块,使用import引入模块,还提供了export default为模块指定默认输出,对应使用import时不需要大括号
/** 定义模块 math.js **/
var basicNum = 0;
var add = function (a, b) {
return a + b;
};
export { basicNum, add };
/** 引用模块 **/
import { basicNum, add } from './math';
function test(ele) {
ele.textContent = add(99 + basicNum);
}
/** export default **/
//定义输出
export default { basicNum, add };
//引入
import math from './math';
function test(ele) {
ele.textContent = math.add(99 + math.basicNum);
}
因为ES6无法在浏览器中执行,所有需要通过babel将不被支持的import编译为能够支持的require
CommonJS和ES6的区别
- CommonJS模块输出的是一个值的拷贝,ES6模块输出的是值的引用
- CommonJS模块是运行时加载,ES6是编译时输出接口
- CommonJS模块的require是同步加载模块的,ES6模块的import命令是异步加载的,有一个独立的模块依赖的解析阶段