为什么需要模块化和模块化规范
模块化可以解决代码之间的变量、函数、对象等命名的冲突/污染问题,良好的模块化设计可以降低代码之间的耦合关系,提高代码的可维护性、可扩展性以及复用性。
模块化规范的作用是为了规范 JavaScript 模块的定义和加载机制,以统一的方式导出和加载模块,降低学习使用成本,提高开发效率。
我们可以从rollup
输出的几种格式开始了解模块化规范。详细可以看这篇文章 (opens in a new tab),这里只总结优缺点。
IIFE 自执行函数(immediately invoked function expression)
- 优点:
- 通过闭包营造了一个“私有”命名空间,防止影响全局,并防止被从外部修改私有变量。
- 简单易懂
- 对代码体积的影响不大
- 缺点:
- 输出的变量可能影响全局变量;引入依赖包时依赖全局变量。
- 需要使用者自行维护 script 标签的加载顺序。
CommonJS (opens in a new tab)
CommonJS 主要是 Node.js 使用,通过 require
同步加载模块,exports
导出内容,同时导出变量为值拷贝。在 CommonJS 规范下,每一个 JS 文件都是独立的模块,每个模块都有独立的作用域,模块里的本地变量都是私有的。
- 优点
- 完善的模块化方案,完美解决了 IIFE 的各种缺点。
- 简单易用
- 可以在任意位置
require
模块 - 支持循环依赖
- 缺点
- 同步的引入方式可能导致浏览器假死。
- 浏览器端使用需要打包
- 难以支持模块静态分析
AMD (opens in a new tab)
AMD,即异步模块定义。AMD 定义了一套 JavaScript 模块依赖异步加载标准,用来解决浏览器端模块加载的问题。AMD 主要是浏览器端使用,通过 define
定义模块和依赖,require
异步加载模块,同时导出变量也为值拷贝,推崇依赖前置。
- 优点
- 解决了 IIFE 的缺点
- 解决了 CommonJS 的缺点
- 依赖异步加载,更快的启动速度
- 支持循环依赖
- 支持插件
- 一套完备的浏览器里 js 文件模块化方案
- 缺点
- 语法相对复杂
- 依赖加载器
- 难以支持模块静态分析
UMD (opens in a new tab)
UMD,即通用模块定义。UMD 主要为了解决 CommonJS 和 AMD 规范下的代码不通用的问题,是 AMD 和 CommonJS 的整合,同时还支持将模块挂载到全局,是跨平台的解决方案。
- 优点
- 抹平了一个包在 AMD 和 CommonJS 里的差异
- 缺点
- 会为了兼容产生大量不好理解的代码。(理解难度与包体积)
ESM (opens in a new tab)
ESM,即 ESModule、ECMAScript Module。官方模块化规范,现代浏览器原生支持,通过 import
加载模块,export
导出内容,在非 default 导出的情况下,导出变量为值的引用,否之亦为值的拷贝。
- 优点
- 支持同步/异步加载
- 语法简单
- 支持模块静态分析
- 支持循环引用
- 缺点
- 兼容性不佳
SystemJS (opens in a new tab)
SystemJS 可以通过各种插件,实现对 AMD、UMD 的加载,并且借助运行时编译器,可以实现对 ES Modules 和 CJS 模块的直接加载。(了解不深)
额外补充
CMD
CMD,即通用模块定义。CMD 的基本逻辑跟 AMD 是一致的,需要使用 sea.js
库,CMD 定义了一套 JavaScript 模块依赖异步加载标准,用来解决浏览器端模块加载的问题。CMD 主要是浏览器端使用,通过 define
定义模块和依赖,require
异步加载模块,推崇依赖就近。
- 优点
- 依赖异步加载,更快的启动速度
- 与 CommonJS 保持很大的兼容性
- 支持循环依赖
- 依赖就近
- 缺点
- 语法相对复杂
- 依赖加载器
- 难以支持模块静态分析
扩展阅读
- 静态分析
静态程序分析(Static program analysis)是指在不运行程序的条件下,进行程序分析的方法。 静态程序分析 - Wiki (opens in a new tab)
简而言之,前文里提到的静态分析就是指在运行代码之前就可判断出代码内有哪些代码使用到了,哪些没有使用到。
- 模块化与工程化:webpack
webpack 同时支持 CommonJS、AMD 和 ESM 三种模块化规范的打包。根据不同规范 webpack 会将模块处理成不同的产物。
- 模块化与工程化:Tree Shaking
Tree Shaking 是一个通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)行为的术语。它依赖于 ES2015 中的 import 和 export 语句,用来检测代码模块是否被导出、导入,且被 JavaScript 文件使用。 Tree Shaking - MDN (opens in a new tab)
简单来说,Tree Shaking 是一种依赖 ESM 模块静态分析实现的功能,它可以在编译时安全的移除代码中未使用的部分(webpack 5 对 CommonJS 也进行了支持,在此不详细展开)。
参考资料
https://juejin.cn/post/7051236803344334862 (opens in a new tab)
https://febook.hzfe.org/awesome-interview/book1/js-module-specs (opens in a new tab)