前端模块化加载

前端模块化加载

四月 30, 2019

面试被问到了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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 定义模块 myModule.js
define(['dependency'], function(){
var name = 'Byron';
function printName(){
console.log(name);
}

return {
printName: printName
};
});

// 加载模块
require(['myModule'], function (my){
  my.printName();
});

CMD

接下来实现的是CMD,即Common Module Definition;要解决的问题是如何按需加载。AMD模块化加载是,需要用到哪些模块,第一次统一加载,当所有模块加载好之后,才开始执行js。而CMD则是,用到的时候,按需加载。

一般写法是:

1
2
3
4
5
6
7
8
9
10
// 定义模块  myModule.js
define(function(require, exports, module) {
var $ = require('jquery.js')
$('div').addClass('active');
});

// 加载模块
seajs.use(['myModule.js'], function(my){

});

在定义myModule.js里,当用到jquery.js时,才会去加载jquery(个人理解,懒得去深究了);

AMD与CMD

参考这篇掘金

简单来说,AMD是同时加载,CMD是按需加载。所以会感觉AMD是异步加载、CMD是同步加载的错觉;

ES6 modules

阮一峰大神的

ES6 modules与CommonJs

  1. CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
  • CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。
  • ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。换句话说,ES6 的import有点像 Unix 系统的“符号连接”,原始值变了,import加载的值也会跟着变。因此,ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。
  1. CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
  • 运行时加载: CommonJS 模块就是对象;即在输入时是先加载整个模块,生成一个对象,然后再从这个对象上面读取方法,这种加载称为“运行时加载”。
  • 编译时加载: ES6 模块不是对象,而是通过export命令显式指定输出的代码,import时采用静态命令的形式。即在import时可以指定加载某个输出值,而不是加载整个模块,这种加载称为“编译时加载”。

CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。