- 什么是模块
- 模块化的好处
- IIFE、AMD、CMD、CommonJS、ES Module
- CommonJS 与 ES Module 区别
- 总结
# 什么是模块
- 将一个复杂的程序依据一定的规则(规范)封装成几个块(文件),并进行组合在一起
- 块的内部数据与实现是私有的,只是向外部暴露一些接口(方法)与外部其它模块通信
# 模块化的好处
模块化可以带来以下好处:
1.解决命名冲突,减少命名空间污染 2.更好的分离,按需加载 3.更高复用性 4.提高代码可维护性
# 立即执行函数(IIFE)
通过函数作用域解决了命名冲突,污染全局作用域的问题。
(function (globalVars) {
globalVars.test = function () {
// 声明各种变量,都不会污染全局作用域
}
})(globalVars);
1
2
3
4
5
2
3
4
5
# AMD
异步加载模块,允许指定回调函数。
浏览器环境,要从服务端加载模块,就必须采用非同步模式,因此浏览器端一般采用 AMD 规范
AMD 规范比 CommonJS 规范在浏览器端的实现要早
// AMD
define(['./a', './b'], function (a, b) {
// 加载模块完毕可使用
a.do();
b.do();
})
1
2
3
4
5
6
2
3
4
5
6
# CMD
专门用于浏览器端,异步加载模块,模块使用时才会加载执行。
整合了 AMD 和 CommonJS 的特点。
// CMD
define(function (require, exports, module) {
// 加载模块
// 可以把 require 写在函数体任何地方实现延迟加载
var a = require('./a');
a.do();
});
1
2
3
4
5
6
7
2
3
4
5
6
7
# CommonJS
最初被应用于 Nodejs,成为 Nodejs 的模块规范。目前也在广泛使用,目前在 Node 中的模块管理已经和 CommonJS 有一些区别了。
// a.js
module.exports = {
a: 1,
};
// 或者
exports.a = 1;
// b.js
var module = require('./a.js');
console.log(module.a) // 1
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
// 其实是包装了一层 IIFE
// module 是 Node 独有的一个变量
module.exports = {
a: 1,
};
1
2
3
4
5
2
3
4
5
// module 的基本实现
var module = {
id: 'xxx', // 去寻找模块
exports: {}, // exports 就是个空对象
};
var exports = module.exports; // exports 和 module.exports 用法相似的原因
var load = function (module) {
// 导出的东西
var a = 1;
module.exports = a;
return module.exports;
};
// 然后 require 时找 id,然后将使用的东西用 IIFE 包装下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
虽然 exports
和 module.exports
用法相同,但是不能对 exports
直接赋值,虽然 exports
和 module.exports
是同一个内存地址,但是对 exports
直接赋值就会导致两者不再指向同一个地址。
# ES Module
原生实现的模块化方案,设计思想是尽量静态化,使得编译时就能确定依赖关系,以及输入和输出变量。AMD 和 CommonJS 都只能在运行时确定这些对象, CommonJS 模块就是对象,输入时必须查找对象属性。
但目前浏览器对 ES6 Module 兼容还不太好,我们平时在 Webpack 中使用的 export
和 import
,会经过 Babel 转换为 CommonJS 规范。
# CommonJS 与 ES Module 区别
- CommonJS 是动态语法支持动态导入,
require(${path}/xx.js)
,可以写在判断里。ES Module 静态语法只能写在顶层,但目前已有提案。 - CommonJS 在导出的是值的拷贝,ES Module 导出的是值的引用,导入值会跟随导出值变化。
- CommonJS 是同步导入,因为用于服务端,文件都在本地,同步导入卡住主线程影响也不大。ES Module 是异步导入,因为用于浏览器,需要下载文件,采用同步会对渲染有大影响。
- CommonJs 的
this
是当前模块,ES Module的this
是undefined
,因为 ES Module 自动采用严格模式,不管你是否声明"use strict";
- CommonJS 模块是运行时加载,输出的是对象,输入时去对象上查找属性。ES6 模块是编译时加载,使静态分析成为可能。
# 总结
- CommonJS 规范主要用于服务端编程,加载模块是同步的,这并不适合在浏览器环境,因为同步意味着阻塞加载,浏览器资源是异步加载的,因此有了 AMD CMD 解决方案
- AMD 规范在浏览器环境中异步加载模块,而且可以并行加载多个模块。不过,AMD规范开发成本高,代码的阅读和书写比较困难,模块定义方式的语义不顺畅
- CMD 规范与 AMD 规范很相似,都用于浏览器编程,依赖就近,延迟执行,可以很容易在 Node.js 中运行。不过,依赖SPM 打包,模块的加载逻辑偏重
- ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案