CommonJS 正在损害 JavaScript
JavaScript,无可争议的 Web 开发之王,正在被破坏 - 不是被竞争对手的语言或革命性的新技术破坏,而是被它自身过去遗留下来的包袱所破坏。这个阴险的破坏者就是 CommonJS,我们容忍了太久了的古老模块系统。
CommonJS 的崛起
在发明大约 15 年后,JavaScript 开始超越浏览器,扩展到服务器端。更大的项目正在使用这种语言构建,JavaScript 需要一种更好的方法来处理大量的源代码。它需要模块化。
2009 年,Mozilla 开发人员 Kevin Dangoor 发出号召。在 “服务器端 JavaScript 需要什么” 中,他阐述了新兴的服务器端 JS 领域缺少许多东西,包括一个模块系统。
JavaScript 需要一种标准方式来包含其他模块,并使这些模块存在于独立的命名空间中。有一些简单的命名空间方法,但没有标准的编程方式来加载模块(一次!)。这非常重要,因为服务器端应用程序可以包含大量代码,并且可能会混合和匹配符合这些标准接口的各个部分。
— Kevin Dangoor,服务器端 JavaScript 需要什么(2009)
在一周内,224 人加入了当时被称为 ServerJS Google 群组,其中包括 npm 创始人 Issac Schlueter 和 Node.js 创建者 Ryan Dahl(他在这里向该群组介绍了 Node)。这个邮件列表将继续规范出 CommonJS 的第一个版本,这个模块系统成为了 Node 的一部分。
提议的 CommonJS 语法(require()
、module.exports
等)与客户端 JavaScript 不一样。这是有意的。Dangoor 想要将 CommonJS 与浏览器 JavaScript 区分开来的意图在他 2009 年在 CommonJS Google 群组中的消息 中表露无遗
我真的认为,服务器端代码的需求与客户端代码的需求足够不同,以至于我们最好从 Python 和 Ruby 中借鉴,而不是从 Dojo 和 jQuery 中借鉴。
除了 Node.js 之外,其他几个早期的服务器端 JavaScript 运行时也采用了 CommonJS,例如 Flusspferd、GPSEE、Narwhal、Persevere、RingoJS、Sproutcore 和 v8cgi(大多数由 CommonJS 核心团队构建)。
但随着 Node.js 成为事实上的服务器端 JavaScript 运行时,并以 CommonJS 作为其主要的模块系统,更广泛的 CommonJS 标准化工作失去了动力。当只有一个主要运行时时,对标准的需求就更少了:Node.js 的实现就成了标准。
回想起来,在我看来,CommonJS 的目标是(或者至少应该)发现 Node,并实现我们在这里构建的东西。有一些错误,因为事后诸葛亮并不能让你如愿以偿,但总的来说,我认为整个 CommonJS 项目可以被认为是成功的。
— Issac Schlueter,对 打破 CommonJS 标准化僵局 的评论(2013)
尽管是默认的模块系统,但 CommonJS 存在一些核心问题
- 模块加载是同步的。每个模块都是一个接一个地加载和执行,按照它们被引用的顺序。
- 难以进行树状摇动,这可以删除未使用的模块并最小化捆绑包大小。
- 不是浏览器原生。您需要捆绑器和转译器才能使所有这些代码在客户端工作。使用 CommonJS,您只能选择 大型构建步骤 或者为客户端和服务器编写单独的代码。
到 2013 年,CommonJS 团队开始逐渐解散。但就在那一年,负责监督核心 JavaScript 语言更新的 TC39 委员会已经在着手 CommonJS 模块的继任者:ECMAScript 模块。
ECMAScript 模块是 Web 为先的
随着 ES6 语言规范 的发布,TC39 委员会终于在 JavaScript 语言中引入了内置的模块系统。目标是构建一个适用于 Web 的单一模块加载器系统,其中包括异步模块加载、与浏览器的兼容性、静态分析和树状摇动。
ES 模块假设它们将通过网络而不是在文件系统中获取数据,从而提供更好的性能和用户体验。
现在,模块加载器系统已内置于语言中,每个人都会同意使用它,这样我们就可以将精力集中在更高级、更重要的问题上,对吗?
…对吗?
Node 决定同时支持 CJS 和 ESM
ES 模块和 Common JS 就像老湾调味料和香草冰淇淋一样搭配在一起
— Myles Borins,摘自关于 模块 模块 模块 的演讲
(我来自马里兰州,所以这对我来说听起来很棒。)
Borins 是 Node ‘模块团队’ 中的一名开发人员,负责在 Node 中实现 ES 模块。尽管成功地将 ESM 添加到 Node 中,但该团队无法就 ESM 与 CJS 之间的互操作性达成明确的共识。然而,Node 无法删除 CJS,因为它已经深深嵌入其中。这意味着互操作性问题被推给了包作者。
以下是一个模块的 package.json
代码片段,需要支持 ESM 和 CJS
“发布同时支持 esm 和 cjs 的包真是太糟糕了” — Wes Bos
其他模块作者使用 dnt 成功地支持了 CommonJS 和 ESM。只需用 TypeScript 编写您的模块,这个构建工具就会将其转换为 Node.js,并发出 ESM/CommonJS/TypeScript 声明文件和 package.json
。
很明显,在 2023 年支持 CommonJS 已成为一个无法忽视的大问题。现在是时候埋葬 CommonJS,转向完全 ESM 的未来。
require
再见了,感谢您所有的 我们设想一个未来,开发者在安装模块后,无需构建步骤就能在 Node.js 或浏览器中运行代码。
— Myles Borins,ESModules 的当前实施和规划状态(2017)
2009 年,CommonJS 正是 JavaScript 所需要的。这个团队解决了一个难题,并强推了一个解决方案,这个解决方案至今每天都被使用数百万次。
但随着 ESM 成为标准,重点转向云原生技术 - 边缘、浏览器和无服务器计算 - CommonJS 已经跟不上时代了。ESM 对于开发者来说是更好的解决方案,因为他们可以编写与浏览器兼容的代码 - 并且用户可以获得更好的最终体验。
不要错过任何更新 - 在 Twitter 上关注我们。