搭建 JS 模块知识体系,掌握 JS 模块化开发

377 阅读5分钟

JavaScript 在一开始并没有模块,在大型的应用程序中,很容易出现全局污染(变量命名冲突)、依赖管理混乱等问题。

后来随着技术的发展,出现了 Node.js ,并实现了 CommonJS 模块化规范。CommonJS 模块化规范适用于服务器。后来 ES6 在语言标准的层面实现了模块功能,叫 ES 模块,ES 模块如今也成为了浏览器和服务器通用的模块化方案。

CommonJS 是由社区提出的一种 JavaScript 的模块化方案,并由 Node.js 借鉴与实现。主要特点是通过 require()module.exports 实现模块的导入和导出。

ES 模块是在语言标准层面上实现的模块化方案。

个人认为,ES 模块的出现意味着 JavaScript 模块化方案的成熟,服务器和浏览器的模块化方案因此得到统一。

ES 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。而 CommonJS 模块只能在运行时确定这些东西。 ES6 的 Tree Shaking (摇树优化)就是借助 ES6 的模块化能力,通过分析模块之间的依赖关系,来判断哪些代码没有被引用,进而删除对应代码。

ES 模块在编译时就完成模块加载,CommonJS 模块在运行时才加载,因此,ES 模块的加载效率要比 CommonJS 模块的高。

在 ES 模块中,主要通过 import 命令导入模块、export 命令导出模块。结合 Webpack 等打包工具,还支持动态导入的语法(import())实现代码分割。

CommonJS 模块与 ES 模块有三个重大的差异:

  • CommonJS 模块输出的是一个值的拷贝,ES 模块输出的是值的引用。

  • CommonJS 模块是运行时加载,ES 模块是编译时输出接口。

  • CommonJS 模块的 require() 是同步加载模块,ES 模块的 import 命令是异步加载,有一个独立的模块依赖的解析阶段。

在依赖关系复杂的大型项目中,很容易出现依赖循环加载的情况。即 a 模块加载了 b 模块,b 模块又加载了 a 模块。因此模块化方案必须要考虑到循环加载的情况。不过开发中应尽量避免模块循环加载的情况,因为出现循环加载,意味着循环加载的模块之间存在强耦合,并且容易出现 bug 。

CommonJS 模块和 ES 模块都支持模块的循环加载,他们的处理方式不一样,返回的结果也不一样。

当 CommonJS 模块遇到循环加载时,会只输出模块代码中已经执行的部分,还未执行的部分不会输出

CommonJS 模块遇到循环加载不会死循环的原因是 CommonJS 模块加载器在加载模块时,会在内存中产生一个模块缓存,当 CommonJS 模块加载器在加载模块时,会先在缓存中查找是否有该模块,如果有就直接在缓存中获取该模块,而不会再次加载该模块,从而避免了无限循环加载的情况。这也是在一个模块中,加载同一个模块多次,该模块的代码只会执行一次的原因。

ES 模块的运行机制与 CommonJS 模块的不一样,ES 模块导出的是值的引用,当模块内部的值变化时,外部能够通过该引用得到模块内部最新的值。当 ES 模块发遇到循环加载时,需要开发者自己保证,真正取值的时候能够取到值

ES 模块在遇到循环加载时不会死循环也是因为缓存

ES 模块在加载过程中会依据模块中使用的 import 语句构建模块依赖图,在构建模块依赖图的过程中会实例化各个模块,创建模块记录,模块记录会放到 模块映射(module map) 中。模块映射(module map)会缓存每个模块实例,在加载模块前,也会先在缓存中查找是否存在相应的模块实例,如果有,则不会执行加载模块的逻辑,因此在循环依赖(加载)中,避免了无限循环加载。

由于 ES 模块化规范出来的比较晚,所以出现了现在 CommonJS 模块和 ES 模块共存的情况。因此在开发中可能会遇到 ES 模块与 CommonJS 模块互相引用的情况。

在 CommonJS 模块中,可以使用动态导入语法(import())导入 ES 模块,在 node.js 22.0.0 版本及之后,则支持了使用 require() 命令导入 ES 模块,当然到目前为止,这个还是实验性的特性。

在 ES 模块中,则可使用 import 命令导入 CommonJS 模块,由于 ES 模块会进行静态分析,而 CommonJS 模块是无法进行静态分析的,所以只能整体加载,无法单一的加载输出项。

80.png

相关文章推荐:

  1. 认识 CommonJS 模块化规范

  2. 掌握 JS 模块化开发:细说 ES Module 模块化规范

  3. 什么,你还不知道 CommonJS 模块与 ES 模块的区别?

  4. JS 模块是如何处理模块的循环加载的?

  5. JS 模块进阶:ES 模块与 CommonJS 模块的互相引用

阅读完上述文章,相信你对 JS 模块化开发相关的知识会有个整体而全面的认识。