在typescript编译器选项#module中,我们有一系列选项,包括nodenextesnext,其中nodenext是实验性的(截至目前).

为什么我们需要这个额外的nodenext选项,而esnext选项似乎已经在使用node.js?

或者换言之,nodenextesnext之间有什么区别?

推荐答案

module and moduleResolution

首先需要澄清的是modulemoduleResolution编译器选项的区别.前者是一个emit设置:tsc愿意向JS发送哪些与模块相关的代码?查看此选项效果的最简单方法是在设置commonjsesnext之间切换:

Input code Output --module commonjs Output --module esnext
import { createSourceFile } from "typescript" "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const typescript_1 = require("typescript"); import { createSourceFile } from "typescript"

虽然此设置基本上控制emits ,但它可以对允许的模块相关输入代码施加限制.例如,您不能以--module es2015以下import fs = require("fs")(或更高ES目标)的样式写入导入,因为ES模块系统中没有require.此外,仅在--module es2022(或更高)或system中允许使用顶级await,因为它需要模块加载系统中的相应支持.

另一方面,--moduleResolution是用来回答以下问题的算法:"给定一个文件系统和一些包含"lodash"导入的输入文件,我应该寻找哪些文件来找到该模块?"显然,查找具有神奇名称node_modules的文件夹的决定与Node有关(尽管为了方便起见,大量非Node工具已经复制了该文件夹),并且不会对每个可能的运行时都是正确的.

Differences in moduleResolution

在这样的背景下,我们准备开始直接回答您的问题.--module nodenext--module esnext之间最大、最明显的区别是前者意味着--moduleResolution nodenext,这是一种新的解析模式,专为 node 具体实现共存的ESM和CJ而设计,而后者does not imply a 104 setting because there is no such corresponding setting in TypeScript right now.换句话说,当你说你使用--module esnext时,你可以写,我们将发出,最新和最好的ES模块代码构造,但我们在决定导入如何解析时不会做任何不同的事情.您可能会继续使用--moduleResolution node,它是为Node实现CJS而设计的.这对你意味着什么?如果你正在为Node编写ESM,你可能会让一些东西在--module esnext--moduleResolution node上工作,但是像package.json exports这样更新的特定于Node的功能将不起作用,而且在编写导入路径时,你很容易就被击中了脚.tsc将根据 node 的CJS规则判断路径,但在运行时, node 将根据其ESM规则判断路径,因为您正在emits ESM.这些算法之间有显著的差异,尤其是后者需要相对导入来使用文件扩展名,而不是删除.js,而index文件没有特殊意义,因此不能仅通过命名目录路径来导入索引文件.

Differences in module

--module设置中观察到的差异本身就有点微妙.正如我之前提到的,在esnext中,你不能写import Foo = require("bar"),因为我们假设没有require.在nodenext中,我们知道给定模块可能是ES模块or,也可能是CJS模块,基于其文件扩展名(.mts)→ .mjs意味着ESM和.cts.cjs表示CJS)和/或最近包装中的type字段.json文件.--module nodenext支持查看这些内容,从而决定给定文件的模块类型,控制我们发出的模块输出类型.如果上述条件导致模块被解释为CJS,则该文件的输出几乎相同(可能相同?)你能从--module commonjs得到什么.如果模块被解释为ESM,那么输出与您从--module esnext获得的结果非常相似,但有一个例外,我可以从头回忆起:您仍然可以写入import Foo = require("bar"),并且编译为:

import { createRequire as _createRequire } from "module";
const __require = _createRequire(import.meta.url);
const Foo = __require("bar");

总结

我认为你问题的答案可以总结如下:

  • node 12+支持CJS和ESM并排运行,如软件包所示.json type和特殊的文件扩展名,所以我们需要一个理解这些内容的模块emits 模式.该模式大致可以被认为是现有commonjsesnext模式之间的基于 node 的 Select 器,并根据 node 定制了一些额外的小差异.
  • Node 12+还为包中的模块说明符如何解析带来了主要的新功能,并针对ESM导入实施了一种不同且更严格的解析算法.如果没有匹配的TypeScript解析模式,面对这些新功能,我们都无法解析,并且允许您编写won’t个ESM解析的路径.

Node.js相关问答推荐

promise 所有映射或反对

查询嵌套数组中的最后一个元素具有特定值的mongoDB集合中的文档

购物车是空的状态|本地存储不更新产品数量|Redux|

为什么在导出的函数中调用node-sqlite3中的数据库方法时不起作用?

设置默认 node 版本

遇到 - TypeError:try 使用 Express(Node.js) 从 JSON 文件访问数据时无法读取未定义的属性(读取帖子)

如何在Node.js的telegraf.js命令中添加参数?

如何获取文件的中间值?

将文件传递到 AWS lambdas(nodejs) + API 网关后重新上传文件

如何找到特定文档并更新数组中特定键的值?

MongoDB Atlas中的聚合触发器不起作用

多个 Axios 请求合并

我怎样才能让`git`失败而不是要求提供凭据

如何在 Nest.js 中使用查询参数?

AWS Kinesis 中的分区键是什么?

如何使用 Mocha 测试正常(非 node 特定)JavaScript 函数?

使用 Node.js 和 Express 进行简单的 API 调用

Meteor - collection.find() 总是返回所有字段

Node.js - 判断是否安装了模块而不实际需要它

在 Node.js 中写入 CSV