在 Node.js 中 选择 CommonjS 还是 ES modules ?
在浏览器 JavaScript 生态系统中,JavaScript 模块的使用依赖于importandexport语句;
ES 模块格式是打包 JavaScript 代码以供重用的官方标准格式,大多数现代 Web 浏览器都原生支持这些模块。
然而,Node.js 默认支持 CommonJS 模块格式。
ES 模块格式在Node.js v8.5.0中被引入。作为一个实验模块,但是,2020 年 5 月,Node.js v12.17.0为所有 Node.js 应用程序提供了 ESM 支持,Node.js 已经稳定支持 ES 模块。
在 Node.js 中使用 ES 模块和 CommonJS 模块的优缺点
ES 模块是 JavaScript 的标准,而 CommonJS 是 Node.js 中的默认
CommonJS 模块系统内置于 Node.js 中。在 Node.js 中引入 ES 模块之前,CommonJS 是 Node.js 模块的标准。因此,有大量使用 CommonJS 编写的 Node.js 库和模块。
旧的 Node.js 版本不支持 ES 模块
使用 ES 模块会导致应用程序与仅支持 CommonJS 模块的早期版本的 Node.js 不兼容
CommonJS 提供模块导入的灵活性
ESModule只能在顶级作用域中导入相比于CommonJS失去了更大的自由度和灵活性
CommonJS 加载模块是同步的,ES 模块是异步的
在所有的 ES 模块导入中,需要明确地将文件扩展名添加到所有文件导入中
这对于 ES 模块是强制性的,就是说require('./')必须写为import .. from './index.js'(在typescript中也必须这样写!)
CommonJS 导入在运行时动态解析require()函数只是在我们的代码执行时运行。因此可以在代码中的任何地方调用它。
对于 ES 模块,导入是静态的,这意味着它们在解析时执行
静态导入可以提高程序的执行效率,出现问题也能第一时间知道
现象
一些著名的库作者开始讨论将他们的包迁移到仅支持 ESM
随着 Node.js v10 在 2021 年 4 月接近其生命周期结束日期,一些著名的库作者开始讨论将他们的包迁移到仅支持 ESM,比如拥有17.9k star的命令行交互工具inquirer,在去年六月已经完全迁入到esm 甚至要求必须使用typescript时必须编译为ESM
拥有8.7k star的ora库也转为ESM,并给出了十分详细的所有情况下的使用方法
但是@tsconfig/node20针对 Node.js 20 的项目提供的推荐编译器选项仍是commonjs
思考
对于仍在使用旧版本 Node.js 的开发人员来说,使用新的 ES 模块是不切实际的。
由于粗略的支持,将现有项目转换为 ES 模块会使应用程序与仅支持 CommonJS 模块的早期版本的 Node.js 不兼容。``
因此,将项目迁移到使用 ES 模块可能不是特别有益。
对于新的 Node.js 项目,ES 模块提供了 CommonJS 的替代方案。ES 模块格式确实提供了一种编写同构 JavaScript 的更简单途径,它可以在浏览器或服务器上运行。是完全可以选择使用ES 模块替代 CommonJS
如果要开始一个新项目,请使用 ES 模块。它已经标准化多年了。自 2020 年 4 月发布的第 14 版以来,NodeJS 对其提供了稳定的支持。您可以在那里找到很多文档和示例。许多包维护者已经发布了支持 ES 模块的库。没有理由不使用它。
如果正在维护一个使用 CommonJS 的现有 NodeJS 项目,情况可能会有所不同。最重要的事实是,目前没有迁移现有代码的压力。CommonJS 仍然是 NodeJS 的默认模块系统,并且没有迹象表明这会很快改变。但是,您可能会在底层使用 CommonJS 时迁移到 ES 模块语法。这可以通过 Babel 或 TypeScript 等工具来完成,并允许您决定在以后的某个时间点更轻松地切换到 ES 模块。
参考文章
Getting Started with (and Surviving) Node.js ESM
CommonJS vs. ES modules in Node.js
How to Setup a TypeScript + Node.js Project