一、CommonJS、AMD、CMD、ES6知识点简单概述
(1)CommonJS
NodeJS是CommonJS规范服务器端的实现,webpack也是CommonJS的形式书写。同步加载,服务器端从磁盘中读取速度快,运行在服务器端没有问题。
(2)AMD
AMD是Asynchronous Module Definition异步模块定义。
基于CommonJS规范的node.JS是服务端模块化的实现。实现浏览器端的模块化就是AMD,且能与服务器端兼容最好。同一个模块在服务器端和浏览器端都可以维护运行,简单方便很多,效率也提高了不少。
浏览器不能兼容CommonJS,于是AMD就出现了。浏览器不能兼容CommonJS的根本原因在于缺少node.JS的四个环境变量:module、exports、requrie、global。
为什么服务器端可以同步加载,浏览器端不能同步加载,需要异步加载?
var math = require('math');
math.add(2,3)
解析:第二行代码在第一行之后运行,必须等到math.js加载完成后运行,如果加载时间特别特别长,整个程序就会卡顿。这里的这里的requrie是同步加载的。浏览器的模块都在服务器端,等待时间取决于网络速度的快慢,等待的时间越长,浏览器响应的时间越长,甚至造成“假死”的状态。服务器端的模块放在本地硬盘中,同步加载读取的时间很快,几乎不会产生什么影响。
(3)CMD
AMD推崇依赖前置,CMD推崇依赖就近,延迟加载。
(4)ES6模块化
ES6模块化采用静态编译,在编译的时候就能确定依赖关系,以及输入和输出的变量。
CommonJS和AMD模块只能运行时确定。
二、AMD规范与CMD规范的区别
①CMD推崇依赖就近,AMD推崇依赖前置
②CMD延迟执行,AMD是提前执行
③CMD性能好,按需加载,当用户有需要在执行。AMD用户体验好,不延迟执行,依赖模块提前加载完毕
//AMD默认推荐的是
define(['./a', './b'], function (a, b) { //依赖前置,必须一开始就写好
a.doSomething()
b.doSomething()
})
//CMD默认推荐的是
define(function (require, exports, module) {
var a = require('./a')
a.doSomething()
var b = require('./b') //依赖就近,按需加载,需要哪个写哪个
b.doSomething()
})
AMD和CMD最大的区别是对依赖模块的执行时机处理不同,注意不是加载的时机或者方式不同
①AMD和CMD都是异步加载模块。AMD依赖前置,js可以提前知道所有的依赖模块,立即加载。CMD就近依赖,模块解析为字符串后才能知道依赖哪些模块。CMD性能好,按需加载,用户需要才加载。AMD用户体验好,模块全部提前加载好。
②AMD加载完模块之后就会立即执行它,所有模块加载完之后进入require回调函数,执行主逻辑。依赖模块的执行顺序和开发人员写的不一样,哪一个模块网速好先下载哪个就先执行,但是主逻辑一定是所有模块加载完成后才执行。
③CMD加载完某个模块后并不执行,只是下载,在所有模块加载完成后进入主逻辑,遇到require语句的时候才执行对应的模块。依赖模块的执行顺序和开发人员写的一样。
三、ES6模块和CommonJS规范区别
①CommonJS支持动态导入,ES6不支持,是静态编译。
②CommonJS同步加载,用于服务端,文件放在本地磁盘,读取速度快。同步导入卡住主线程也并无影响。ES6是异步加载,用于浏览器端,不能同步加载,会导致页面渲染,用户体验差。
③CommonJS模块输出的是值拷贝,内部的变化影响不到值的变化。ES6模块输出的是值引用,原始值变化,加载的值也会跟着变化,ES6模块是动态引用,并且不会缓存值。
④CommonJS模块是运行时加载,ES6模块是编译时输出接口。CommonJS模块就是对象,输入时先加载整个模块,生成一个对象,然后从对象读取方法。ES6模块不是对象,export输出指定代码,import导入加载某个值,而不是整个模块。
⑤关于模块顶层的this指向问题,在CommonJS顶层,this指向当前模块;而在ES6模块中,this指向undefined。
⑥ES6模块当中,是支持加载CommonJS模块的。但是反过来,CommonJS并不能requireES6模块,在NodeJS中,两种模块方案是分开处理的。
四、加载模式的总结
(1)CommonJS加载模式—同步/运行时加载
CommonJS加载模块是同步的。输入的时候加载整个模块,生成一个对象,从这个对象上读取方法。子模块完成加载,才能执行后面的操作。输入的值是被输出的值的拷贝,父模块引入子模块,引入的是子模块的值拷贝,模块的内部变化无法影响这个值。
(2)AMD加载模式—异步加载、依赖前置
AMD在浏览器端异步加载,AMD推崇依赖前置,加载完模块之后就会立即执行它。
(3)CMD加载模式—异步加载、依赖就近
CMD在浏览器端异步加载,CMD推崇依赖就近,加载完模块不会立即执行,只是加载,等到需要的时候才会执行。
(4)ES6加载模式—静态编译
ES6静态编译,在编译的时候就能确定依赖,编译的时候输出接口。export输出指定代码,import某个值不是整个模块。
五、ES6、CommonJS循环引用问题
什么是循环引用?循环加载指的是a脚本的执行依赖b脚本,b脚本的执行依赖a脚本。
①CommonJS模块是加载时执行。一旦出现某个模块被“循环加载”,就只输出已经执行的部分,没有执行的部分不会输出。
②ES6模块对导出模块,变量,对象是动态引用,遇到模块加载命令import时不会去执行模块,只是生成一个指向被加载模块的引用