,当谈到 JavaScript 的模块化规范时,我们不可避免地会提到 CommonJS。多年来,CommonJS 在 Node.js 生态系统中扮演着至关重要的角色,它让开发者能够方便地使用模块化的代码组织方式。然而,随着 ES Modules(ESM)的出现和日益普及,人们开始思考一个问题:CommonJS 是否注定会消失?在本文中,我们将探讨 CommonJS 的现状和未来,并评估它与ESM之间的关系。是否真的可以全面过渡到ESM,抛弃CommonJS?让我们一起来看看。,JavaScript 出现大约 15 年后,开始从浏览器扩展到服务端。许多更大的项目选择使用 JavaScript 进行构建,而 JavaScript 需要一种更好的方法来处理大量源代码,它需要模块化。,2009 年,Mozilla 的开发者 Kevin Dangoor 发出了号召。在 “服务端 JavaScript 需要什么”一文中,他列举出了服务端 JavaScript 所缺少的许多内容,包括模块系统。,在一周内,包括 npm 的创始人 Issac Schlueter 和 Node.js 的创建者 Ryan Dahl 在内,共有 224 人加入了当时称为 ServerJS 的 Google 群组。该邮件列表将继续规范 CommonJS 的第一个版本,该模块系统成为 Node.js 的一部分。,提议的 CommonJS 语法(require()、module.exports等)看起来并不像客户端JavaScript。这是经过设计的。Dangoor意图将CommonJS与浏览器JavaScript区分开来,这一点从他 2009 年在 CommonJS Google 群组上的消息中就可以看出来:,除了 Node.js 之外,其他几个早期的服务端 JavaScript 运行时也采用了 CommonJS,例如 Flusspferd、GPSEE、Narwhal、Persevere、RingoJS、Sproutcore 和 v8cgi(大多数由核心 CommonJS 团队构建)。,但随着 Node.js 成为真正的服务端 JavaScript 运行时,并以 CommonJS 作为其主要模块系统,更广泛的 CommonJS 标准化工作失去了动力。当只有一个主要运行时时,对标准的需求就会减少:Node.js 实现就成为了标准。,尽管 CommonJS 是默认的模块系统,但它仍然存在一些核心问题:,到 2013 年,CommonJS 小组开始逐渐解体。但到了那一年,负责监督 JavaScript 核心语言更新的 TC39 委员会已经开始开发 CommonJS 模块的后继者:ECMAScript 模块。,随着ES6语言规范的发布,TC39委员会最终引入了一个直接嵌入到JavaScript语言中的模块系统。其目标是构建一个适用于Web的单一模块加载器系统,包括异步模块加载、与浏览器兼容、静态分析和 tree shaking。ES模块假设它们将从网络中获取数据,而不是从文件系统中获取,以提供更好的性能和用户体验。,现在,由于模块加载系统已经直接集成到语言中,大家都会使用它,这样就可以将精力集中在更高级别、更重要的问题上了。,Borins 是 Node“模块团队”的开发人员之一,负责在 Node.js 中实现 ES 模块。尽管成功地将 ESM 添加到 Node,但团队未能就 ESM 和 CJS 之间的互操作性达成明确的共识。然而 Node 无法摆脱 CJS,因为它的嵌入程度如此之深。这意味着互操作性问题被推给了软件包作者。,以下是支持 ESM 和 CJS 所需的模块 package.json 的片段:,,显然,支持 CommonJS 已经成为不容忽视的问题。但是,CommonJS 真的是一无是处嘛?,对于较大的应用,ES 模块速度较慢。与 require 不同,要么需要在使用语句时加载整个模块图,要么需要使用表达式等待每个导入。例如,如果想要延迟加载一个包以在函数中使用,代码必须返回一个 Promise。,ES模块为了绑定导入和导出,需要进行两次处理。整个模块图会被解析和分析,然后代码会被评估。这些被分为不同的步骤。正是这种方式使得ES模块中的“实时绑定”成为可能。,考虑以下两个简单的文件:,Babel 是一个由大量文件组成的包,因此比较这两个文件的运行时间是评估与模块解析相关的性能成本的好方法。结果如下:,在 Bun 中(一个新的 JavaScript 运行时),使用 CommonJS 加载 Babel 的速度大约比使用 ES 模块快 2.4 倍,相差了 85ms。在无服务器冷启动的情况下,这个差距是巨大的。在 Node.js 中,差异为 1.8 倍(约 60 毫秒)。,CommonJS 允许动态模块加载——可以有条件地 require() 文件,或者 require() 动态构造的路径/说明符,或者在函数体中 require()。这种灵活性在需要动态加载的场景中非常有利,例如插件系统或基于用户交互的延迟加载特定组件。,ES 模块提供了具有类似属性的动态 import() 函数。从某种意义上说,它的存在证明了 CommonJS 的动态方法具有实用性并受到开发人员的重视。,发布到 npm 的数百万个模块已经使用 CommonJS,其中许多都是:,(a) 不再积极维护。,(b) 对现有项目很重要。,可能永远不会达到所有包都可以使用 ES 模块的地步。不支持CommonJS的运行时或框架将失去巨大的价值。,尽管 ES Modules(ESM)的出现给予了 CommonJS 微弱的竞争压力,但我们可以得出结论:CommonJS 不会轻易消失。,CommonJS 在 Node.js 生态系统中的广泛应用以及大量已发布在 npm 上的模块都使用了 CommonJS 规范,这使得 CommonJS 成为不可忽视的存在。很多模块虽然不再得到积极维护,但对于现有项目仍具有重要性。因此,在实际应用中,对于不支持 CommonJS 的运行时或框架来说,忽视 CommonJS 将意味着错失巨大的价值。,尽管 ES Modules 在语言层面提供了更强大的静态分析和优化能力,并且被认为是 JavaScript 的未来,但 CommonJS 仍然具有优势。它简单易用,适合动态加载和代码重用。因此,在目前的情况下,我们仍然需要将 CommonJS 视为一个重要的模块化规范。,无论是 CommonJS 还是 ES Modules,它们都有各自的特点和适用场景。我们不应该试图完全取代 CommonJS,而是应该在技术发展的过程中平衡实用性和未来趋势。只有这样,才能更好地利用 JavaScript 的模块化能力,为项目和生态系统带来最大的价值。,参考资料:
文章版权声明
1 原创文章作者:cmcc,如若转载,请注明出处: https://www.52hwl.com/29173.html
2 温馨提示:软件侵权请联系469472785#qq.com(三天内删除相关链接)资源失效请留言反馈
3 下载提示:如遇蓝奏云无法访问,请修改lanzous(把s修改成x)
4 免责声明:本站为个人博客,所有软件信息均来自网络 修改版软件,加群广告提示为修改者自留,非本站信息,注意鉴别