前端模块化加载
面试被问到了AMD、CMD的区别,因为平时用考虑这个问题,所以没有回答上来。本来觉得这是个过时的技术,所以没有很认真对待。但是了解过后,至少可以把这当做前端模块化历史去了解一下。并且可以深入了解一下CommonJs、以及现在的ES6modules。
之所以要单独记录一下,因为网上搜到的解释比较刻意,而阮一峰大神解释的虽然循序渐进、通俗易懂,但是由于历史原因,只解释了CommonJs与AMD。
CommonJs
CommonJs是一个由 Mozilla 的工程师 Kevin Dangoor 在2009年1月创建的标准规范,然后当年Node.js诞生的时候,就参考了CommonJs来实现Node.js的模块化。
语法是require('module')
let math = require('math')
math.random();
CommonJs是同步引用的,当需要引入模块时,会等待模块加载成功,才会继续往下执行。
另一篇说得也挺好
AMD
第二个诞生的是AMD,全称为Asynchronous Module Definition。要解决的是如何前端模块化
。因为浏览器资源都是互联网上的,不可能像CommonJs一样同步引用,所以AMD参考了CommonJs实现了一个异步引用的模块化加载模式。
语法是:require(['module'], callback)
require(['math'], function(math){
math.random();
})
require.js是AMD的一个实现。
一般是这样用的(虽然已经不重要了)
1 | // 定义模块 myModule.js |
CMD
接下来实现的是CMD,即Common Module Definition;要解决的问题是如何按需加载
。AMD模块化加载是,需要用到哪些模块,第一次统一加载,当所有模块加载好之后,才开始执行js。而CMD则是,用到的时候,按需加载。
一般写法是:
1 | // 定义模块 myModule.js |
在定义myModule.js里,当用到jquery.js时,才会去加载jquery(个人理解,懒得去深究了);
AMD与CMD
简单来说,AMD是同时加载,CMD是按需加载。所以会感觉AMD是异步加载、CMD是同步加载的错觉;
ES6 modules
ES6 modules与CommonJs
- CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
- CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。
- ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令
import
,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。换句话说,ES6 的import
有点像 Unix 系统的“符号连接”,原始值变了,import
加载的值也会跟着变。因此,ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。
- CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
- 运行时加载: CommonJS 模块就是对象;即在输入时是先加载整个模块,生成一个对象,然后再从这个对象上面读取方法,这种加载称为“运行时加载”。
- 编译时加载: ES6 模块不是对象,而是通过
export
命令显式指定输出的代码,import
时采用静态命令的形式。即在import
时可以指定加载某个输出值,而不是加载整个模块,这种加载称为“编译时加载”。
CommonJS 加载的是一个对象(即module.exports
属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。