CommonJS
特点
只能用于服务端。脚本被require
时就会执行模块中的代码,这个特性在服务端没有问题,但是如果引入一个模块就要等待他执行完才能执行后面的代码,在浏览器端就会有很大的问题。已加载的模块不会重复加载。
声明
// 方式1
module.exports.add = function(){}
// 方式2
module.exports = {
add: function(){}
}
// 方式3
exports.add = function(){}
// 默认导出
module.exports = function(){}
使用
// 方式1、2、3对应的导出方式
const math = require('./math');
math.add();
// 默认导出
const test = require('./math');
test();
循环依赖
require
的模块会立即执行;- 已加载的模块不会重复加载。
// a.js
console.log('a starting');
exports.done = false;
const b = require('./b.js');
console.log('in a, b.done = %j', b.done);
exports.done = true;
console.log('a done');
// b.js
console.log('b starting');
exports.done = false;
const a = require('./a.js');
console.log('in b, a.done = %j', a.done);
exports.done = true;
console.log('b done');
// main.js
console.log('main starting');
const a = require('./a.js');
const b = require('./b.js');
console.log('in main, a.done=%j, b.done=%j', a.done, b.done);
- 执行
main.js
首先输出main starting
; - 引入
a
模块,立即执行,打印出a starting
,此时a.done = false
; - 在
a
模块中引入b
模块,立即执行,打印出b starting
,此时b.done = false
; - 由于已经加载过
a
模块,所以被缓存,缓存的值是false
,故打印in b, a.done = false
; b
模块导出done = true
,并打印b done
;- 回到
a
模块,由于已经加载过b
模块,所以b
的值是true
,即in a, b.done = true
; a
模块导出done = true
,并打印a done
;- 回到
main
,a b
模块都已经被执行和缓存,直接打印in main, a.done=true, b.done=true
。
故最后的打印结果应该是:
main starting
a starting
b starting
in b, a.done = false
b done
in a, b.done = true
a done
in main, a.done = true, b.done = true
module.exoprts 和 exports的区别
// module的实现
var module = {
id: 'xxx', // module的唯一识别,类似id
exports: {}
}
var exports = module.exports;
从上面的例子可以看出, exports=module.exports
,两者都指向了同一个内存地址,所以module.exports
改变,exports
也会改变;但是若是对exports
直接赋值,两者就不会指向同一个内存地址,故修改不会对module.exports
起效。
AMD
特点
用于浏览器的模块加载。它采用异步加载方式加载模块,加载的过程中不影响后面的语句。
声明
define(function(){
return {
add: function(){}
}
})
使用
require(['math'], function(math){
console.log(math.add());
})
循环依赖
当出现循环依赖时候,采用的是强制忽略。
当A依赖B,然后B依赖了A,此时B获取到的A是未定义状态。故B一定会被优先执行完成。
UMD
特点
解决跨平台的问题,即可以运行在服务端,也可以运行在浏览器上。
实现
// if the module has no dependencies, the above pattern can be simplified to
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define([], factory);
} else if (typeof exports === 'object') {
// Node. Does not work with strict CommonJS, but
// only CommonJS-like environments that support module.exports,
// like Node.
module.exports = factory();
} else {
// Browser globals (root is window)
root.returnExports = factory();
}
}(this, function () {
// Just return a value to define the module export.
// This example returns an object, but the module
// can return a function as the exported value.
return {};
}));
ES6 Module
特点
在编译时就可以确定模块的依赖关系。如果遇到import
时不会执行模块,而是生成一个引用,等到真正需要时,才到模块里面执行语句取值。
声明
export function add(){}
// 或者
export default {
add
}
使用
import { add } from './test';
add();
循环依赖
它并不关心有没有循环依赖,他并不需要产生结果,他只需要给你一个引用即可,至于是否能取到值,那么就需要开发者自己来保证了。
总结
commonJS
的特点是在require
时就会执行代码,会影响后续代码的执行,并且执行过的模块会被缓存,所以commonJS
只适用于服务端;AMD
用异步加载方式加载模块,加载的过程中不影响后面的语句,所以适用于浏览器端;UMD
是一种结合CommonJS
和AMD
两者的模块化规范;ES6 Module
会在编译的时候通过import
确定引用关系,在真正需要的时候才会执行。
补充
commonJS
支持动态导入,即require(${path}/xx.js)
,但是ES6 Module
不支持;Commonjs
导出时时值拷贝,就算导出的值改变了,导入的值也不会改变,但是ES6 Module
采用的是实时绑定的方式,导入导出的值都指向同一个内存地址,所以导出的值会随导出的值变化。
参考
小结AMD,CMD,UMD,CommandJS,ES Module
若有错误,欢迎指出,感谢~