前言
本篇我们重点介绍以下四种模块加载规范:
- AMD
- CMD
- CommonJS
- ES6 模块
最后再延伸讲下 Babel 的编译和 webpack 的打包原理。
require.js
在了解 AMD 规范之前,我们先来看看 require.js 的使用方式。
项目目录为:
* project/
* index.html
* vender/
* main.js
* require.js
* add.js
* square.js
* multiply.js
index.html 的内容如下:
require.js
Content
data-main="vender/main" 表示主模块是 vender 下的 main.js。
main.js 的配置如下:
// main.js
require(['./add', './square'], function(addModule, squareModule) {
console.log(addModule.add(1, 1))
console.log(squareModule.square(3))
});
require 的第一个参数表示依赖的模块的路径,第二个参数表示此模块的内容。
由此可以看出,主模块依赖 add 模块和 square 模块。
我们看下 add 模块即 add.js 的内容:
// add.js
define(function() {
console.log('加载了 add 模块');
var add = function(x, y) {
return x + y;
};
return {
add: add
};
});
requirejs 为全局添加了 define 函数,你只要按照这种约定的方式书写这个模块即可。
那如果依赖的模块又依赖了其他模块呢?
我们来看看主模块依赖的 square 模块, square 模块的作用是求出一个数字的平方,比如输入 3 就返回 9,该模块依赖一个乘法模块,该乘法模块即 multiply.js 的代码如下:
// multiply.js
define(function() {
console.log('加载了 multiply 模块')
var multiply = function(x, y) {
return x * y;
};
return {
multiply: multiply
};
});
而 square 模块就要用到 multiply 模块,其实写法跟 main.js 添加依赖模块一样:
// square.js
define(['./multiply'], function(multiplyModule) {
console.log('加载了 square 模块')
return {
square: function(num) {
return multiplyModule.multiply(num, num)
}
};
});
require.js 会自动分析依赖关系,将需要加载的模块正确加载。
requirejs 项目 Demo 地址:https://github.com/mqyqingfeng/Blog/tree/master/demos/ES6/module/requirejs
而如果我们在浏览器中打开 index.html,打印的顺序为:
加载了 add 模块
加载了 multiply 模块
加载了 square 模块
2
9
AMD
在上节,我们说了这样一句话:
requirejs 为全局添加了 define 函数,你只要按照这种约定的方式书写这个模块即可。
那这个约定的书写方式是指什么呢?
指的便是 The Asynchronous Module Definition (AMD) 规范。
所以其实 AMD 是 RequireJS 在推广过程中对模块定义的规范化产出。
你去看 AMD 规范 的内容,其主要内容就是定义了 define 函数该如何书写,只要你按照这个规范书写模块和依赖,require.js 就能正确的进行解析。
sea.js
在国内,经常与 AMD 被一起提起的还有 CMD,CMD 又是什么呢?我们从 sea.js 的使用开始说起。
文件目录与 requirejs 项目目录相同:
* project/
* index.html
* vender/
* main.js
* require.js
* add.js
* square.js
* multiply.js
index.html 的内容如下:
sea.js
Content
main.js 的内容如下:
// main.js
define(function(require, exports, module) {
var addModule = require('./add');
console.log(addModule.add(1, 1))
var squareModule = require('./square');
console.log(squareModule.square(3))
});
add.js 的内容如下:
// add.js
define(function(require, exports, module) {
console.log('加载了 add 模块')
var add = function(x, y) {
return x + y;
};
module.exports = {
add: add
};
});
square.js 的内容如下:
define(function(require, exports, module) {
console.log('加载了 square 模块')
var multiplyModule = require('./multiply');
module.exports = {
square: function(num) {
return multiplyModule.multiply(num, num)
}
};
});
multiply.js 的内容如下:
define(function(require, exports, module) {
console.log('加载了 multiply 模块')
var multiply = function(x, y) {
return x * y;
};
module.exports = {
multiply: multiply
};
});
跟第一个例子是同样的依赖结构,即 main 依赖 add 和 square,square 又依赖 multiply。
seajs 项目 Demo 地址:https://github.com/mqyqingfeng/Blog/tree/master/demos/ES6/module/seajs
而如果我们在浏览器中打开 index.html,打印的顺序为:
加载了 add 模块
2
加载了 square 模块
加载了 multiply 模块
9
CMD
与 AMD 一样,CMD 其实就是 SeaJS 在推广过程中对模块定义的规范化产出。
你去看 CMD 规范的内容,主要内容就是描述该如何定义模块,如何引入模块,如何导出模块,只要你按照这个规范书写代码,sea.js 就能正确的进行解析。
AMD 与 CMD 的区别
从 sea.js 和 require.js 的例子可以看出:
1.CMD 推崇依赖就近,AMD 推崇依赖前置。看两个项目中的 main.js:
// require.js 例子中的 main.js
// 依赖必须一开始就写好
require(['./add', './square'], function(addModule, squareModule) {
console.log(addModule.add(1, 1))
console.log(squareModule.square(3))
});
// sea.js 例子中的 main.js
define(function(require, exports, module) {
var addModule = require('./add');
console.log(addModule.add(1, 1))
// 依赖可以就近书写
var squareModule = require('./square');
console.log(squareModule.square(3))
});
2.对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。看两个项目中的打印顺序:
// require.js
加载了 add 模块
加载了 multiply 模块
加载了 square 模块
2
9
// sea.js
加载了 add 模块
2
加载了 square 模块
加载了 multiply 模块
9
AMD 是将需要使用的模块先加载完再执行代码,而 CMD 是在 require 的时候才去加载模块文件,加载完再接着执行。
感谢
感谢 require.js 和 sea.js 在推动 Javascript 模块化发展方面做出的贡献。
CommonJS
AMD 和 CMD 都是用于浏览器端的模块规范,而在服务器端比如 node,采用的则是 CommonJS 规范。
导出模块的方式:
var add = function(x, y) {
return x + y;
};
module.exports.add = add;
引入模块的方式:
var add = require('./add.js');
console.log(add.add(1, 1));
我们将之前的例子改成 CommonJS 规范:
// main.js
var add = require('./add.js');
console.log(add.add(1, 1))
var square = require('./square.js');
console.log(square.square(3));
// add.js
console.log('加载了 add 模块')
var add = function(x, y) {
return x + y;
};
module.exports.add = add;
// multiply.js
console.log('加载了 multiply 模块')
var multiply = function(x, y) {
return x * y;
};
module.exports.multiply = multiply;
// square.js
console.log('加载了 square 模块')
var multiply = require('./multiply.js');
var square = function(num) {
return multiply.multiply(num, num);
};
module.exports.square = square;
CommonJS 项目 Demo 地址:https://github.com/mqyqingfeng/Blog/tree/master/demos/ES6/module/commonJS
如果我们执行 node main.js,打印的顺序为:
加载了 add 模块
2
加载了 square 模块
加载了 multiply 模块
9
跟 sea.js 的执行结果一致,也是在 require 的时候才去加载模块文件,加载完再接着执行。
CommonJS 与 AMD
引用阮一峰老师的《Javascript 标准参考教程(alpha)》:
CommonJS 规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。
AMD规范则是非同步加载模块,允许指定回调函数。
由于 Node.js 主要用于服务器编程,模块文件一般都已经存在于本地硬盘,所以加载起来比较快,不用考虑非同步加载的方式,所以 CommonJS 规范比较适用。
但是,如果是浏览器环境,要从服务器端加载模块,这时就必须采用非同步模式,因此浏览器端一般采用 AMD 规范。
ES6
ECMAscript2015 规定了新的模块加载方案。
导出模块的方式:
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export {firstName, lastName, year};
引入模块的方式:
import {firstName, lastName, year} from './profile';
我们再将上面的例子改成 ES6 规范:
目录结构与 requirejs 和 seajs 目录结构一致。
ES6
Content
注意!浏览器加载 ES6 模块,也使用


