他们都是用于在模块化定义中使用的,AMD、CMD、CommonJs是ES5中提供的模块化编程的方案,import/export是ES6中定义新增的。
一、CommonJS
服务端模块规范,Node.js采用了这个规范。
一个单独文件就是一个模块,通过require方法来同步加载要依赖的模块,然后通过exports或则module.exports来导出需要暴露的接口。
require("module");
require("../file.js");
exports.doStuff = function() {};
module.exports = someValue;
加载模块是同步的,只有加载完成后才能执行后面的操作,也就是当要用到该模块了,现加载现用,不仅加载速度慢,而且还会导致性能、可用性、调试和跨域访问等问题。Node.js主要用于服务器编程,加载的模块文件一般都存在本地硬盘,加载起来比较快,不用考虑异步加载的方式,因此,CommonJS规范比较适用。然而,这并不适合在浏览器环境,同步意味着阻塞加载,浏览器资源是异步加载的,因此有了AMD CMD解决方案。
二、AMD
异步模块定义,AMD推崇依赖前置(在定义模块的时候就要声明其依赖的模块)。
它要在声明模块的时候指定所有的依赖 dependencies ,并且还要当做形参传到factory 中,对于依赖的模块提前执行,依赖前置。
其核心接口是:define(id?, dependencies?, factory) 。
- id:可选参数,用来定义模块的标识,如果没有提供该参数,脚本文件名(去掉拓展名)
- dependencies:是一个当前模块依赖的模块名称数组
- factory:工厂方法,模块初始化要执行的函数或对象。如果为函数,它应该只被执行一次。如果是对象,此对象应该为模块的输出值。
实现:RequireJS; curl;
// 定义模块 myModule.js
define(['dependency'], function(){
var name = 'Byron';
function printName(){
console.log(name);
}
return {
printName: printName
};
});
// 加载模块
require(['myModule'], function (my){
my.printName();
});
优点:适合在浏览器环境中异步加载模块。可以并行加载多个模块。
缺点:提高了开发成本,并且不能按需加载,而是必须提前加载所有的依赖。
三、CMD - 同步模块定义
同步模块定义,CMD推崇依赖就近(只有在用到某个模块的时候再去require——按需加载)。
实现:Sea.js ;coolie
// 定义模块 myModule.js
define(function(require, exports, module) {
var $ = require('jquery.js')
$('div').addClass('active');
});
// 加载模块
seajs.use(['myModule.js'], function(my){
});
优点:依赖就近,延迟执行 可以很容易在 Node.js 中运行;
缺点:依赖 SPM 打包,模块的加载逻辑偏重;
AMD和CMD区别:
AMD在加载完成定义(define)好的模块就会立即执行,所有执行完成后,遇到require才会执行主逻辑。(提前加载)
CMD在加载完成定义(define)好的模块,仅仅是下载不执行,在遇到require才会执行对应的模块。(按需加载)
AMD用户体验好,因为没有延迟,CMD性能好,因为只有用户需要的时候才执行。
四、ES6 Module
ES6自带模块化,可以使用import关键字引入模块,通过export关键字到处模块,功能较之前几个方案更为强大,但是由于ES6目前无法在浏览器中执行,所以,需要通过babel将不支持的import编译为当前收到广泛支持的require。
ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。
CommonJS vs ES6 Module:
- CommonJS支持动态导入,也就是 require(${path}/xx.js),ES6目前不支持
- CommonJS是同步导入,因为用于服务端,文件都在本地,同步导入即使卡住主线程影响也不大。而ES6是异步导入,因为用于浏览器,需要下载文件,如果也采用同步导入会对渲染有很大影响。
- ES6模块中的值属于【动态只读引用】。只读:import的变量是只读的,不论是基本数据类型还是复杂数据类型,在不能在导入的文件修改,复杂数据类型可以修改属性值不会报错,但是不建议这么做。动态:原始值发生变化,import加载的值也会发生变化。不论是基本数据类型还是复杂数据类型。
CommonJS导出时都是值拷贝,就算导出的值变了,导入的值也不会改变,当导出的是复杂数据类型时,属于浅拷贝,由于引用相同,所以修改会互相影响。使用require命令加载某个模块时,就会运行整个模块的代码。当再次使用require命令加载同一个模块时,不会再执行该模块,而是取到缓存之中的值,除非手动清除系统缓存。 - ES6编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,在运行时才能确定。
网友评论