再次梳理AMD、CMD、CommonJS、ES6 Module的区别

AMD (RequireJS)

AMD全称是Asynchronous Module Definition,即异步模块加载机制。后来由该草案的做者以RequireJS实现了AMD规范,因此通常说AMD也是指RequireJS。css

RequireJS的基本用法

经过define来定义一个模块,使用require能够导入定义的模块。vue

//a.js
//define能够传入三个参数,分别是字符串-模块名、数组-依赖模块、函数-回调函数
define(function(){
    return 1;
})
复制代码
// b.js
//数组中声明须要加载的模块,能够是模块名、js文件路径
require(['a'], function(a){
    console.log(a);// 1
});
复制代码

RequireJS的特色 对于依赖的模块,AMD推崇依赖前置,提早执行。也就是说,在define方法里传入的依赖模块(数组),会在一开始就下载并执行。jquery

CMD ( SeaJS )

CMD是SeaJS在推广过程当中生产的对模块定义的规范,在Web浏览器端的模块加载器中,SeaJS与RequireJS并称,SeaJS做者为阿里的玉伯。webpack

SeaJS的基本用法

//a.js
/* * define 接受 factory 参数,factory 能够是一个函数,也能够是一个对象或字符串, * factory 为对象、字符串时,表示模块的接口就是该对象、字符串。 * define 也能够接受两个以上参数。字符串 id 表示模块标识,数组 deps 是模块依赖. */
define(function(require, exports, module) {
  var $ = require('jquery');

  exports.setColor = function() {
    $('body').css('color','#333');
  };
});
复制代码
//b.js
//数组中声明须要加载的模块,能够是模块名、js文件路径
seajs.use(['a'], function(a) {
  $('#el').click(a.setColor);
});
复制代码

SeaJS的特色

对于依赖的模块,CMD推崇依赖就近,延迟执行。也就是说,只有到require时依赖模块才执行。git

CommonJS

CommonJS规范为CommonJS小组所提出,目的是弥补JavaScript在服务器端缺乏模块化机制,NodeJS、webpack都是基于该规范来实现的。es6

CommonJS的基本用法

//a.js
module.exports = function () {
  console.log("hello world")
}
复制代码
//b.js
var a = require('./a');

a();//"hello world"

//或者

//a2.js
exports.num = 1;
exports.obj = {xx: 2};

//b2.js
var a2 = require('./a2');

console.log(a2);//{ num: 1, obj: { xx: 2 } }
复制代码

CommonJS的特色

  • 全部代码都运行在模块做用域,不会污染全局做用域;
  • 模块是同步加载的,即只有加载完成,才能执行后面的操做;
  • 模块在首次执行后就会缓存,再次加载只返回缓存结果,若是想要再次执行,可清除缓存
  • require返回的值是被输出的值的拷贝,模块内部的变化也不会影响这个值。

ES6 Module

ES6 Module是ES6中规定的模块体系,相比上面提到的规范, ES6 Module有更多的优点,有望成为浏览器和服务器通用的模块解决方案。github

ES6 Module的基本用法

//a.js
var name = 'lin';
var age = 13;
var job = 'ninja';

export { name, age, job};
复制代码
//b.js
import { name, age, job} from './a.js';

console.log(name, age, job);// lin 13 ninja
复制代码
//或者

//a2.js
export default function () {
  console.log('default ');
}

//b2.js
import customName from './a2.js';
customName(); // 'default'
复制代码

ES6 Module的特色(对比CommonJS)

  • CommonJS模块是运行时加载,ES6 Module是编译时输出接口;
  • CommonJS加载的是整个模块,将全部的接口所有加载进来,ES6 Module能够单独加载其中的某个接口;
  • CommonJS输出是值的拷贝,ES6 Module输出的是值的引用,被输出模块的内部的改变会影响引用的改变;
  • CommonJS this指向当前模块,ES6 Module this指向undefined;

目前浏览器对ES6 Module兼容还不太好,咱们平时在webpack中使用的export/import,会通过babel转换为CommonJS规范。web

总结:

  • AMD 依赖前置,提早执行 (require.js),语法是define,require
  • CMD 依赖就近,延迟执行 (sea.js),语法是 define,seajs.use([],cb)
  • CommonJs 语法 module.exports=fn或者exports.a=1; 经过require('./a1')来引入
  • CommonJs 模块首次执行会被缓存,再次加载只返回缓存结果,require返回的值是输出值的拷贝(对于引用类型是浅拷贝)
  • es6 module 语法是 export {...}, import ...from..., export输出的是值得引用。
  • NodeJS、webpack都是基于commonJs该规范来实现的

一些小问题

  • 在vue项目中可能存在,咱们A模块导入了B模块,C模块也导入了B模块,那么B模块在一个项目中被导入了屡次的场景,模块化机制是如何处理的呢?

若是你是经过commonJS来require导入的模块,那么,首次导入会被缓存,后续的导入都是取的第一次的缓存结果。若是你是经过es6 import导入的模块,那么导入的都是值的引用。因此,导入屡次是在最终打包的时候,只打了一个而并不会重复打入的状况。。数组

  • 若是深刻理解,先须要多使用,而后仍是须要把CommonJS的内部实现,与 es6的内部实现过一遍。

保留个问题: B导入模块A,咱们本身如何实现?不用模块化

  1. 把模块A的代码复制到了模块B,显然不靠谱,这样会被复制出来的代码量太大
  2. 动态建立script标签,先让A引入并执行。而后在引入B
  3. es6 Module是导出的值的引用。这个是如何实现的?
  4. 屡次重复导入如何处理?
  5. 循环引用如何处理?
  6. ......

总结-Commonjs 和 es6 module 的区别

  • Commonjs是同步导入,因用于服务端,文件都在本地,同步导入即便卡住主线程影响也不大,后者是异步导入,由于用于浏览器端,需下载文件,若是采用同步导入对渲染会有很大影响。
  • CommonJS 在导出时都是值的拷贝,就算导出的值变了,导入的值也不会变。若是想更新,必须从新导入一回。
  • ES Module 导入导出的值指向同一个内存地址。因此,导入值也会随着导出值变化。
  • ES Module 会编译成 require/exports来执行。
  • 一个导出的是值的拷贝,一个导出的是值的引用
  • 一个是同步导入,一个是异步导入

参考:juejin.im/post/5db95e…浏览器

请多多指点其中的问题点,使用的文章不少,难的是,真正的理解其内部机制与如何实现的?从模块做者的角度考虑问题,多思考,学习才能掌握要领。从而实现本身的工具。

码字不易,以为还行,给star一下,您的star,是我前进的动力哈~

github

相关文章
相关标签/搜索