什么是模块化?
模块化是一种处理复杂系统分解为更好的可管理模块的方式。
所谓的模块化开发就是封装细节,提供使用接口,彼此之间互不影响,每个模块都是实现某一特定的功能。
模块化开发的基础就是函数。
模块化开发使用代码耦合度降低,模块化的意义在于最大化的设计重用,以最少的模块、零部件,更快速的满足更多的个性化需求。
优点:
- 避免命名冲突
- 提高可复用性
- 支持按需加载
- 提高可维护性
- 提高可调试性
- 提高可测试性
缺点:
大量使用带来性能损耗
- 系统分层,调用链会很长
- 模块间发送消息会很耗性能
为什么需要模块化?
在 WEB 开发的早期,为了团队协作和代码维护的方 便,许多开发者会选择将 JavaScript 代码分开写在不同 的文件里面,然后通过多个 script 标签来加载它们。
<script src="./a.js"></script>
<script src="./b.js">
</script> <script src="./c.js"></script>
虽然每个代码块处在不同的文件中,但最终所有 JS 变量 还是会处在同一个全局作用域下,这时候就需要额外注意由于作用域变量提升所带来的问题。
<script>
// a.js
var num = 1;
setTimeout(() => console.log(num), 1000);
</script>
<script>
// b.js
var num = 2;
</script>
模块化规范
1、AMD规范
(Asynchronous Module Definition)
RequireJS遵循的是AMD规范。AMD规范是异步加载,依赖前置,特点是准备充分,但加载会较慢。使用define()定义模块,require()加载模块,有缓存,用于浏览器端。
2、CMD规范
(Common Module Definition)
SeaJS遵循的是CMD规范。CMD规范也会异步加载,不同的是CMD依赖就近,特点是首次加载很快。CMD规范也是使用define()定义模块,require()加载模块,用于浏览器端,但和AMD规范思想不同,写法也不同。
3、CommonJS规范
CommonJS规范的特点是加载模块顺序按照词法解析的顺序加载,是同步加载的(服务端主要本地读取文件),使用require加载模块,module.exports导出模块,有缓存,用于Nodejs服务端,module可省略,但不推荐。
4、ESModule规范
ESModule的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系。使用import和export来导入导出,ES6还提供了一个default,用来提供默认的export。特点是加载模块存储的是值的引用,所以全局只有一份,加载模块也是异步的。
ES6的module吸收了CommoneJS和AMD两者的优点,兼容两标准的规范,浏览器和服务端均可使用。
5、UMD规范
(Universal Module Definition)
UMD是一种 CommonJS/AMD/CMD 的兼容写法。它是为了让模块同时兼容CommonJS/AMD/CMD 规范而出现的,多被一些需要同时支持浏览器端和服务端引用的第三方库所使用。通过 define.amd/define.cmd/module 等判断当前支持什么方式,都不行就挂载到 window 全局对象上面去。
(function (root, factory) {
if (typeof define === 'function' && (define.amd || define.cmd)) {
//AMD,CMD
define(['b'], function(b){
return (root.returnExportsGlobal = factory(b))
});
} else if (typeof module === 'object' && module.exports) {
//Node, CommonJS之类的
module.exports = factory(require('b'));
} else {
//公开暴露给全局对象
root.returnExports = factory(root.b);
}
}(this, function (b) {
return {};
}));
后模块化时代
通过前面的分析我们可以看出来,使用ESModule
的模块明显更符合 JS 开发的历史进程,因为任何一个支持 JS 的环境,随着对应解释器的升级,最终一定会支持 ESModule 的标准。但是,WEB 端受制于用户使用的浏览器版本,我们并不能随心所欲的随时使用 JS 的最新特性。
为了能让我们的新代码也运行在用户的老浏览器中,社区涌现出了越来越多的工具,它们能静态将高版本规范的代码编译为低版本规范的代码,最为大家所熟知的就是babel
。
对于模块化相关的 import 和 export 关键字,babel 最终会将它编译为包含 require 和 exports 的 CommonJS 规范。这就造成了另一个问题,这样带有模块化关键词的模块,编译之后还是没办法直接运行在浏览器中,因为浏览器端并不能运行 CommonJS 的模块。为了能在 WEB 端直接使用 CommonJS 规范的模块,除了编译之外,我们还需要一个步骤叫做打包(bundle)。
所以打包工具比如 webpack / rollup ,编译工具 babel 它们之间的区别和作用就很清楚了。
- 打包工具主要处理的是JS不同版本间模块化的区别编译工具主要处理的是 JS 版本间语义的问题
- 如果使用了 ESModule : 必须使用 webpack 和 babel, 如果是 AMD 或 CommonJS : 只用 webpack即可
参考资料
JavaScript温故之AMD/CMD/UMD/CommonJS/ES6Module
The difference between module.exports and exports
评论区