- CommonJS是nodejs也就是服务器端广泛使用的模块化机制,模块必须通过module.exports 导出对外的变量或接口,通过require() 来导入其他模块的输出到当前模块作用域中,此方式是同步的
- AMD 是 RequireJS 在推广过程中对模块定义的规范化产出,提前执行(异步加载:依赖先执行)+延迟执行
- CMD 是 SeaJS 在推广过程中对模块定义的规范化产出,延迟执行(运行到需加载,根据顺序执行)
下面来详细讲一下他们之间的区别:
CommonJS
CommonJS模块加载是同步的,后面的模块必须等前面的加载完成才能执行。
定义模块:
// 模块 a.js
const name = 'foo'
module.exports = {
name,
text: 'bar'
}
加载模块:
// 模块 b.js
// 引用核心模块或者第三方包模块,不需要写完整路径
const path = require('path');
// 引用自定义模块可以省略.js
const { name, text } = require('./a');
console.log(name, text);
// 输出 foo bar
CommonJS模块的加载机制是,输入的是被输出的值的拷贝。也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。看如下例子:
// b.js
let age = 1;
setTimeout(() => {
age = 18;
}, 10);
module.exports = {
age
}
// a.js
const b = require('./b');
console.log(b.age);
setTimeout(() => {
console.log(b.age);
console.log(require('./b').age);
}, 100);
// 执行:node a.js
// 执行结果:
// 1
// 1
// 1
还有,CommonJS模块重复引入的模块并不会重复执行,再次获取模块只会获得之前获取到的模块的缓存。
AMD
全称:Asynchronous Module Definition,中文翻译:异步模块定义,javascript原生不支持这种规范,使用AMD规范进行页面开发需要用到对应的库函数,鼎鼎大名的就是RequireJS,
定义模块:
// 独立模块
define(function(){
...
return {
//返回接口
}
})
// 非独立模块
define(['foo','bar'],function(foo, bar){
...
return {
//返回接口
}
})
独立模块即定义的模块不依赖其他模块,非独立模块就是会依赖其他模块。我们定义的模块依赖foo和bar,第二个参数是一个函数,仅当依赖加载成功后才会调用,函数的参数与前面的依赖数组一一对应,函数必须返回一个对象,给其他模块调用。
加载模块:
AMD加载模块也是用require,由于是异步的所以只能用回调函数的方式:
require(['foo','bar'], function(foo,bar){
...
})
回调函数中才能使用依赖的模块,这里是在定义的时候加载模块了,相当于把依赖前置。
require()里面可以配置一些参数:
require.config({
paths: {
"backbone": "vendor/backbone",
"underscore": "vendor/underscore"
},
shim: {
"backbone": {
deps: [ "underscore" ],
exports: "Backbone"
},
"underscore": {
exports: "_"
}
}
});
- paths: 指定模块的位置,可以是文件路径,也可以是一个网址
- shim: 有些库不兼容AMD的写法,可以配置shim来解决,可以理解成“垫片”,帮助require.js加载非AMD规范的库
CMD
即Common Module Definition通用模块定义,这个规范也是国内发展起来的,有个浏览器的实现SeaJS,CMD和AMD要解决的问题都是一样的,只不过在模块定义方式和模块加载(可以说运行、解析)时机上有所不同。
和其他规范一样,CMD中也是一个模块一个文件:
define(function(require, exports, module) {
// 模块代码
});
require是可以把其他模块导入进来的一个参数;而exports是可以把模块内的一些属性和方法导出的;module 是一个对象,上面存储了与当前模块相关联的一些属性和方法。
AMD是依赖关系前置,在定义模块的时候就要声明其依赖的模块,CMD是按需加载依赖,只有在用到某个模块的时候再去require,看看CMD的例子:
// model1.js
define(function (require, exports, module) {
console.log('model1 entry');
exports.getHello = function () {
return 'model1';
}
});
// model2.js
define(function (require, exports, module) {
console.log('model2 entry');
exports.getHello = function () {
return 'model2';
}
});
// main.js
define(function(require, exports, module) {
var model1 = require('./model1'); //在需要时声明
console.log(model1.getHello());
var model2 = require('./model2'); //在需要时声明
console.log(model2.getHello());
});
<script src="sea.js"></script>
<script>
seajs.use('./main.js')
</script>
CommonJS主要用于服务端,模块文件都存在本地硬盘,所以加载起来比较快,不用考虑用异步加载,但是在浏览器中,受限于网络原因,更合理的方案应该是异步加载。为了方便模块化开发,这时武林中出现了AMD和CMD两大门派,分别代表了两种不同的思想,一是依赖前置,二是按需加载依赖,各有利弊,这两大门派都像争夺武林盟主,按理说是要比武论高下的,但半路杀出个程咬金,选举委员会直接自己推了一个规范:ES Module,告知天下以后要听ES Module的,他才是大哥。
这小子又是谁?
ES Module
ES modules(ESM)是 JavaScript 官方的标准化模块系统,能和CommonJS混合使用,意味着能在node环境下执行,因为他是标准,所以未来很多浏览器都会支持。
模块定义:
// module.js
export function name() {
return 'this is name';
}
export function foo() {
return 'foooooo';
}
模块加载:
// index.js
import { name, foo } from './module.js';
console.log(name(), foo());
加载模块的方式还有很多种,比如:
// 加载单个模块
import {sum} from './example.js'
// 加载多个
import {sum,multiply} from './example.js'
// 导入整个模块,别创建一个别名
import * as example from './example.js'
在ESM中模块导出是值的引用,看如下例子:
// b.js
export let age = 1;
setTimeout(() => {
age = 2;
}, 10);
// a.js
import { age } from './b.js';
console.log(age);
setTimeout(() => {
console.log(age);
import('./b.js').then(({ age }) => {
console.log(age);
})
}, 100);
// 执行结果:
// 1
// 2
// 2
import/export使用有一个限制,就是不能在其他语句/表达式的内部使用,比如if语句里面,所以一般都在最底部,原因是ESM使用javascript引擎静态分析。
至此,ESM已经是事实上的盟主,使用该规范无论是node环境还是浏览器环境都能很好兼容,而且前端也不要再引入requirejs或sea.js来进行模块开发。
上面就是我要讲的关于CommonJS,AMD和CMD还有ESM的内容,如果大家感兴趣,也可以关注我的功zhong号:正义的程序猿 ??
本文暂时没有评论,来添加一个吧(●'◡'●)