提纲
js 模块化的方案
说到打包不得不提的就是 js 的语法缺陷:在 es6 出来之前,js 语言原生是不支持模块化的。当然了,js 语言的设计者 Bredan Eich 的初衷是 js 只是一个小脚本语言,花了10天就开发出来了。估计他也没有想到 js 会有这么一天。所以后面也演化出了各种 js 模块化解决方案
CommonJS 规范
当 node 出现的时候,js 就可以运行在服务器上了,也具备了编写大型复杂应用的基础。所以就需要一个模块化方案。而 node 中的模块化就是对 CommonJS 规范的实现
在 CommonJS 规范中,文件是模块的最小单位,通过 module.exports 导出模块(可以通过 exports 直接导出,但不推荐),通过 require 来引入模块,它还有如下特点:
- 模块的引入是同步的
- 模块引入具有缓存机制,第一次引入时,会依次顺序执行模块内容,后续再引用时,则直接使用缓存
- module 代表当前模块,每个模块内部都存在
- __dirname 代表模块当前的路径
- __filename 代表模块当前所在的文件路径+文件名
- global 是全局变量
- 模块中定义的普通变量的运行在模块作用域中,不会污染全局作用域
一个符合 CommonJS 规范的模块导入导出例子如下:
// ================ 导出对象 ========================
// module.js
module.exports = {
a: '1',
b: '2'
};
console.log(module);
console.log(__dirname);
console.log(__filename);
// index.js
const {a, b} = require('./module'); // a = '1', b = '2';
// ================ 导出函数等其他形式 ================
// module.js
function add(a, b) {
return a + b;
}
module.exports = add;
// index.js
const add = require('./module');
这种同步引入模块的方式对于运行在服务器端的 node 来说并没有什么不妥。但对于浏览器端,为了不阻塞显示,显然是采用异步的方式更加的合适
AMD
AMD 全称 Asynchronous Module Definition,是一种异步加载规范。上文说到 CommonJS 规范中的同步加载的特性并不适合浏览器环境,所以 AMD 规范就应运而生了
在 AMD 规范中,通过 define 定义模块,通过 require 来引入模块,如下:
// math.js
define('math', [add], function () {
function add(a, b) {
return a + b;
}
return add;
});
// index.js
require(['math'], function(math) {
math.add(1, 2);
});
其中 require 接收两个参数:要加载的模块列表、加载成功后的回调函数 callback
注意:AMD 规范目前是有这两个函数库:require.js 和 curl.js,所以在使用 AMD 模块方案的时候,需要引入这两个库文件之一
CMD
CMD 全称 Common Module Definition,和 AMD 类似,也是运行在浏览器端的一种异步加载。不同的是,CMD 推崇的是依赖后置,即浏览器先渲染页面,然后再加载模块。和 AMD 正好相反
CMD 规范中,同样的,一个文件就是一个模块:
- 使用 define 来定义模块,参数既可以是工厂函数,也可以是对象等其他参数:define(factory) 或者 define({ name: 'a' })。也可以指明依赖模块:define(id?, deps?, factory),id 就是模块标识,deps 则是依赖其他模块的数组
- define 为工厂函数时,默认有3个参数:require, exports, module。require 来引入其他模块,exports 来定义本模块的对外接口。module 存储了当前模块的一些信息,比如 id,dependencies 等
- 依赖就近原则,不需要提前申明用到的模块,哪里用到就直接通过 require 来引入该模块
// math.js 模块。依赖 module1.js 和 module.js 模块
// 这里根据依赖就近原则,直接使用 require 来加载外部模块
define(function(require, exports, module) {
// 依赖外部模块 module1.js,并且同步引入外部模块
var module1 = require('./module1.js');
// 依赖外部模块 module.js,并且异步引入外部模块
require.async('./module.js', function(m2) {});
// 本模块导出的
export.add = function(a, b) {return a + b};
});
// main.js
sea.use(['math.js'], function(math) {
math.add(1, 2);
});
注意:CMD 规范由 sea.js 实现,所以使用前需要引入 sea.js 文件
综上:CMD 和 AMD 最大的不同就是依赖就近原则了
UMD
UMD 全称 Universal Module Definition,即通用模块定义规范,旨在解决跨平台模块方案(即兼容 CommonJS、AMD、CMD 等模块方案)。它的实现方式比较简单,核心就是根据当前所处的环境来使用对应的模块导出、导入方案。如下:
(function(root, factory) {
if (typeof module === 'object' && typeof module.exports === 'object') {
console.log('是commonjs模块规范,nodejs环境')
module.exports = factory();
} else if (typeof define === 'function' && define.amd) {
console.log('是AMD模块规范,如require.js')
define(factory())
} else if (typeof define === 'function' && define.cmd) {
console.log('是CMD模块规范,如sea.js')
define(function(require, exports, module) {
module.exports = factory()
})
} else {
console.log('没有模块环境,直接挂载在全局对象上')
root.umdModule = factory();
}
}(this, function() {
return {
name: '我是一个umd模块'
}
}))
ES6Module
ES6 在 js 语言层面上实现了模块化的功能,旨在为浏览器和服务器使用。ES6 模块化规范如下:
- 使用 export 导出模块
- 使用 import 导入模块
注意:和 CommonJS 不同的是,ES6 是动态引入的,不会缓存值
参考
【前端工程化:CommonJS、AMD、CMD、UMD和ES Modules的区别】blog.csdn.net/leelxp/arti…
【Javascript 模块化编程(二):AMD 规范】www.ruanyifeng.com/blog/2012/1…
【AMD 规范实例】www.cnblogs.com/fsg6/p/1314…
【CMD 模块定义】blog.csdn.net/weixin_3816…
【可能是最详细的UMD模块入门指南】www.jianshu.com/p/9f5a0351a…