一个提出五年的 Node.js 模块问题,终被解决!

开发 前端
ES Modules 在文件顶层可以使用 Top-Level Await,该方法看之前的介绍,是在使用 esm 加载器加载的 .mjs 文件上使用 require 的功能时,使用了与 esm Top-Level Await 相同的权衡。

一直以来 Node.js 中存在一个问题,CommonJS 与 ES Modules 如何更好的共存? 是令大多数 Node.js 开发者头疼的问题。

问题

当在 ES Modules 模块中引入 CommonJS 模块代码,一切是 Ok 的。如下代码所示:

// c.js
module.exports = {
  moduleName: 'a'
}
// m.mjs
import C_Module from './c.js'
console.log(C_Module); // { moduleName: 'a' }

换一种方式,让 CommonJS 引入 ES Modules,如下代码所示:

// m.mjs
export default 'm'
// c.js
const M_Module = require('./m.mjs')
console.log(M_Module);

终端运行 node c.js,会得到如下提示

图片图片

ERR_REQUIRE_ESM 这个错误太熟悉不过了,它困惑了很多的 Node.js 开发者,为什么换个顺序就不行?

看到的很多答案是这样的 “不支持使用 require 加载 ES 模块,因为 ES 模块是异步执行的”,后面大家就默认了 “CommonJS 是同步,ES Modules 是异步” 这样的一个规则。

2019 提出后很遗憾未能继续推进

CommonJS 模块如何加载 ES Modules 模块,这个问题 2019 年就已经提出,参考 “Support requiring .mjs files” https://github.com/nodejs/node/pull/30891 这个问题在当时没有被解决。

图片图片

ES Modules 在文件顶层可以使用 Top-Level Await,该方法看之前的介绍,是在使用 esm 加载器加载的 .mjs 文件上使用 require 的功能时,使用了与 esm Top-Level Await 相同的权衡。

这意味着:如果可能,所有执行和评估都是同步进行的,通过立即展开执行的组件承诺。这意味着任何现有的代码都不应该有可观察到的行为变化,因为到目前为止还不存在任何异步模块。问题在于,一旦使用需要异步执行的模块,它必须让出事件循环来执行该操作,这反过来又允许其他代码在异步操作之后的继续执行之前执行,这对于现在变成了异步模块的调用者是可观察到的。如果这对你的调用者很重要,那么意味着将你的模块执行异步化可能被视为库的破坏性更改,但实际上,对于大多数调用者来说,这并不重要。而且,由于当前的生态系统,零个模块是异步执行的,因此在有异步执行的模块之前,这种方法没有任何缺点,因为没有执行会改变人们今天所期望的(当然,除了不再错误地要求("./foo.mjs"))。

最后,问题被关闭了,原因是 “因为纯粹从技术角度来看,目前尝试在事件循环已经运行时旋转它是行不通的”。

图片图片

问题是挺错综复杂的,感兴趣的可以去看看 2019 年提出的这个 Issue。

2024 由 Joyee Cheung's 提出解决方案

2019 ~ 2024 这一令大多数开发者头疼的问题,由 Node.js 的维护者成员 “Joyee Cheung” 再次提出了解决方案。参考 Issue “module: support require()ing synchronous ESM graphs” https://github.com/nodejs/node/pull/51977

图片图片

参考 Joyee Cheung 博客的介绍 https://joyeecheung.github.io/blog/2024/03/18/require-esm-in-node-js/

图片图片

去年年底左右,Joyee Cheung 发现了 ESM 的求值可以基于语法同步,而只是 Node.js 将异步性扔到加载过程中后,便与 @GeoffreyBooth 开始讨论重新启动同步 require(esm)。

因此,才有了这个 PR。与 2019 年的 PR 相比主要区别在于它试图将 require(esm) 的范围保持小型,并且仅支持加载同步 ESM。

后续会通过 --experimental-require-module 标志启用,则加载的 ECMAScript 模块 require() 时要满足以下要求:

  • 在最接近的 package.json 文件或 .mjs 扩展中明确标记为 ES 模块,具有 "type": "module" 字段
  • 完全同步(不包含 Top-Level Await)

有网友就在下面问了,这能向后移至到 Node.js 18? 大家还是很喜欢这个功能的!Node.js v20 可能还有希望,这要取决于 Node.js 的发布团队,期待下个 Node.js 版本!

图片图片

这一问题的解决对 Node.js 模块来讲是里程碑式的,很敬佩 Joyee Cheung 的才华!

参考

  • https://github.com/nodejs/node/pull/30891
  • https://github.com/nodejs/node/pull/51977
  • https://joyeecheung.github.io/blog/2024/03/18/require-esm-in-node-js/
责任编辑:武晓燕 来源: Nodejs技术栈
相关推荐

2023-06-30 23:25:46

HTTP模块内存

2011-10-25 09:28:30

Node.js

2022-01-07 08:00:00

Node.js开发Web

2021-12-28 20:04:23

Node.js开发JavaScript

2021-09-26 05:06:04

Node.js模块机制

2020-08-07 10:40:56

Node.jsexpress前端

2020-10-23 06:40:31

Node.js前端开发

2020-04-15 15:48:03

Node.jsstream前端

2019-12-17 11:40:44

Node.js模块前端

2024-03-15 09:26:59

2011-06-17 10:29:04

Nodejavascript

2022-06-05 13:52:32

Node.jsDNS 的原理DNS 服务器

2021-09-15 19:02:42

Node.jsFs模块

2022-01-02 06:55:08

Node.js ObjectWrapAddon

2014-08-01 10:24:11

2020-08-24 08:07:32

Node.js文件函数

2022-10-18 18:43:40

Node.js低代码

2021-02-10 07:38:43

Node.js后端框架

2022-12-14 14:40:27

Node.js开发应用程序

2011-12-09 11:16:48

Node.js
点赞
收藏

51CTO技术栈公众号