- assert 断言
- async_hooks 异步钩子
- async_hooks/context 异步上下文
- buffer 缓冲区
- C++插件
- C/C++插件(使用 Node-API)
- C++嵌入器
- child_process 子进程
- cluster 集群
- CLI 命令行
- console 控制台
- Corepack 核心包
- crypto 加密
- crypto/webcrypto 网络加密
- debugger 调试器
- deprecation 弃用
- dgram 数据报
- diagnostics_channel 诊断通道
- dns 域名服务器
- domain 域
- Error 错误
- events 事件触发器
- fs 文件系统
- global 全局变量
- http 超文本传输协议
- http2 超文本传输协议 2.0
- https 安全超文本传输协议
- inspector 检查器
- Intl 国际化
- module 模块
- module/cjs CommonJS 模块
- module/esm ECMAScript 模块
- module/package 包模块
- net 网络
- os 操作系统
- path 路径
- perf_hooks 性能钩子
- permission 权限
- process 进程
- punycode 域名代码
- querystring 查询字符串
- readline 逐行读取
- repl 交互式解释器
- report 诊断报告
- stream 流
- stream/web 网络流
- string_decoder 字符串解码器
- test 测试
- timers 定时器
- tls 安全传输层
- trace_events 跟踪事件
- tty 终端
- url 网址
- util 实用工具
- v8 引擎
- vm 虚拟机
- wasi 网络汇编系统接口
- worker_threads 工作线程
- zlib 压缩
Node.js v16.20.2 文档
- Node.js v16.20.2
- 目录
-
导航
- assert 断言
- async_hooks 异步钩子
- async_hooks/context 异步上下文
- buffer 缓冲区
- C++插件
- C/C++插件(使用 Node-API)
- C++嵌入器
- child_process 子进程
- cluster 集群
- CLI 命令行
- console 控制台
- Corepack 核心包
- crypto 加密
- crypto/webcrypto 网络加密
- debugger 调试器
- deprecation 弃用
- dgram 数据报
- diagnostics_channel 诊断通道
- dns 域名服务器
- domain 域
- Error 错误
- events 事件触发器
- fs 文件系统
- global 全局变量
- http 超文本传输协议
- http2 超文本传输协议 2.0
- https 安全超文本传输协议
- inspector 检查器
- Intl 国际化
- module 模块
- module/cjs CommonJS 模块
- module/esm ECMAScript 模块
- module/package 包模块
- net 网络
- os 操作系统
- path 路径
- perf_hooks 性能钩子
- permission 权限
- process 进程
- punycode 域名代码
- querystring 查询字符串
- readline 逐行读取
- repl 交互式解释器
- report 诊断报告
- stream 流
- stream/web 网络流
- string_decoder 字符串解码器
- test 测试
- timers 定时器
- tls 安全传输层
- trace_events 跟踪事件
- tty 终端
- url 网址
- util 实用工具
- v8 引擎
- vm 虚拟机
- wasi 网络汇编系统接口
- worker_threads 工作线程
- zlib 压缩
- 其他版本
模块:包#>
【Modules: Packages】
介绍#>
【Introduction】
一个包是由 package.json 文件描述的文件夹树。该包由包含 package.json 文件的文件夹及其所有子文件夹组成,直到遇到下一个包含另一个 package.json 文件的文件夹,或者一个名为 node_modules 的文件夹。
【A package is a folder tree described by a package.json file. The package
consists of the folder containing the package.json file and all subfolders
until the next folder containing another package.json file, or a folder
named node_modules.】
本页面为编写 package.json 文件的包作者提供指导,同时提供 Node.js 定义的 package.json 字段参考。
【This page provides guidance for package authors writing package.json files
along with a reference for the package.json fields defined by Node.js.】
确定模块系统#>
【Determining module system】
当作为初始输入传递给 node,或通过 import 语句或 import() 表达式引用时,Node.js 将把以下内容视为 ES 模块:
【Node.js will treat the following as ES modules when passed to node as the
initial input, or when referenced by import statements or import()
expressions:】
- 具有
.mjs扩展名的文件。 - 当最近的父级
package.json文件包含顶层"type"字段且其值为"module"时,具有.js扩展名的文件。 - 作为
--eval参数传入的字符串,或通过STDIN管道传递给node,并使用标志--input-type=module。
Node.js 会将所有其他形式的输入例如 .js 文件(其最近的父级 package.json 文件中没有顶层的 "type" 字段)或没有使用 --input-type 标志的字符串输入视为 CommonJS。此行为是为了保持向后兼容性。然而,现在 Node.js 同时支持 CommonJS 和 ES 模块,因此最好在可能的情况下明确指定。Node.js 会在作为初始输入传递给 node 时,或当通过 import 语句、import() 表达式或 require() 表达式引用时,将以下内容视为 CommonJS:
【Node.js will treat as CommonJS all other forms of input, such as .js files
where the nearest parent package.json file contains no top-level "type"
field, or string input without the flag --input-type. This behavior is to
preserve backward compatibility. However, now that Node.js supports both
CommonJS and ES modules, it is best to be explicit whenever possible. Node.js
will treat the following as CommonJS when passed to node as the initial input,
or when referenced by import statements, import() expressions, or
require() expressions:】
- 具有
.cjs扩展名的文件。 - 当最近的父文件“package.json”文件包含顶层字段
"type"且值为“commonjs”时,文件扩展名为“.js”。 - 以参数形式传入
--eval或--print的字符串,或通过STDIN管道传给node,并使用标志--input-type=commonjs。
即使在所有源都是 CommonJS 的包中,包的作者也应该包含 "type" 字段。明确指定包的 type 可以使包在 Node.js 的默认类型发生变化时保持兼容,同时也能让构建工具和加载器更容易确定包中的文件应该如何被解释。
【Package authors should include the "type" field, even in packages where
all sources are CommonJS. Being explicit about the type of the package will
future-proof the package in case the default type of Node.js ever changes, and
it will also make things easier for build tools and loaders to determine how the
files in the package should be interpreted.】
模块加载器#>
【Modules loaders】
Node.js 有两个系统用于解析说明符和加载模块。
【Node.js has two systems for resolving a specifier and loading modules.】
有 CommonJS 模块加载器:
【There is the CommonJS module loader:】
- 它是完全同步的。
- 它负责处理
require()调用。 - 它可以被动态修改。
- 它支持将文件夹作为模块。
- 在解析指定符时,如果没有找到完全匹配项,它会尝试添加扩展名(
.js、.json,最后是.node),然后尝试解析 将文件夹作为模块。 - 它将
.json视为 JSON 文本文件。 .node文件被解释为通过process.dlopen()加载的已编译的插件模块。- 它将所有缺少
.json或.node扩展名的文件视为 JavaScript 文本文件。 - 它不能用于加载 ECMAScript 模块(尽管可以 从 CommonJS 模块加载 ECMAScript 模块)。当用它来加载不是 ECMAScript 模块的 JavaScript 文本文件时,它会将其作为 CommonJS 模块加载。
有 ECMAScript 模块加载器:
【There is the ECMAScript module loader:】
- 它是异步的。
- 它负责处理
import语句和import()表达式。 - 它不能被猴子补丁,但可以使用 加载器钩子 进行自定义。
- 它不支持将文件夹作为模块,目录索引(例如
'./startup/index.js')必须完整指定。 - 它不进行扩展名搜索。当指定的文件 URL 是相对或绝对路径时,必须提供文件扩展名。
- 它可以加载 JSON 模块,但需要导入断言。
- 它只接受
.js、.mjs和.cjs扩展名的 JavaScript 文本文件。 - 它可以用于加载 JavaScript CommonJS 模块。此类模块会通过
cjs-module-lexer进行处理,以尝试识别命名导出,如果可以通过静态分析确定,则可以使用这些导出。导入的 CommonJS 模块会将其 URL 转换为绝对路径,然后通过 CommonJS 模块加载器进行加载。
package.json 和文件扩展名#>
【package.json and file extensions】
在一个包中,package.json "type" 字段定义了 Node.js 应该如何解释 .js 文件。如果 package.json 文件没有 "type" 字段,.js 文件将被视为 CommonJS。
【Within a package, the package.json "type" field defines how
Node.js should interpret .js files. If a package.json file does not have a
"type" field, .js files are treated as CommonJS.】
'package.json' '类型''的'“模块”值告诉Node.js解释“.js” 该包内的文件使用ES 模块语法。
【A package.json "type" value of "module" tells Node.js to interpret .js
files within that package as using ES module syntax.】
"type" 字段不仅适用于初始入口点 (node my-app.js),还适用于通过 import 语句和 import() 表达式引用的文件。
【The "type" field applies not only to initial entry points (node my-app.js)
but also to files referenced by import statements and import() expressions.】
// my-app.js, treated as an ES module because there is a package.json
// file in the same folder with "type": "module".
import './startup/init.js';
// Loaded as ES module since ./startup contains no package.json file,
// and therefore inherits the "type" value from one level up.
import 'commonjs-package';
// Loaded as CommonJS since ./node_modules/commonjs-package/package.json
// lacks a "type" field or contains "type": "commonjs".
import './node_modules/commonjs-package/index.js';
// Loaded as CommonJS since ./node_modules/commonjs-package/package.json
// lacks a "type" field or contains "type": "commonjs".
以 .mjs 结尾的文件总是作为 ES 模块 加载,无论最近的父级 package.json 是什么。
【Files ending with .mjs are always loaded as ES modules regardless of
the nearest parent package.json.】
以 .cjs 结尾的文件总是作为 CommonJS 加载,无论最近的父级 package.json 是什么。
【Files ending with .cjs are always loaded as CommonJS regardless of the
nearest parent package.json.】
import './legacy-file.cjs';
// Loaded as CommonJS since .cjs is always loaded as CommonJS.
import 'commonjs-package/src/index.mjs';
// Loaded as ES module since .mjs is always loaded as ES module.
.mjs 和 .cjs 扩展名可以在同一个包中混合使用不同类型:
【The .mjs and .cjs extensions can be used to mix types within the same
package:】
- 在“type”: “module”包中,Node.js可以通过命名为“.cjs”扩展名来解释某个文件为CommonJS(因为“.js”和“.mjs”文件在“module”包中被视为ES模块)。
- 在一个
"type": "commonjs"的包中,可以通过将特定文件命名为.mjs扩展名来指示 Node.js 将其解释为 ES 模块(因为在"commonjs"包中,.js和.cjs文件都被视为 CommonJS)。
--input-type 标志#>
【--input-type flag】
当设置了 --input-type=module 标志时,作为 --eval(或 -e)参数传入的字符串,或通过 STDIN 管道传入 node 的字符串,将被视为 ES 模块。
【Strings passed in as an argument to --eval (or -e), or piped to node via
STDIN, are treated as ES modules when the --input-type=module flag
is set.】
node --input-type=module --eval "import { sep } from 'node:path'; console.log(sep);"
echo "import { sep } from 'node:path'; console.log(sep);" | node --input-type=module
为了完整性,还有 --input-type=commonjs,用于将字符串输入明确地作为 CommonJS 运行。如果未指定 --input-type,这是默认行为。
【For completeness there is also --input-type=commonjs, for explicitly running
string input as CommonJS. This is the default behavior if --input-type is
unspecified.】
确定包管理器#>
【Determining package manager】
虽然所有的 Node.js 项目在发布后都应该可以通过所有包管理器安装,但它们的开发团队通常被要求使用某一个特定的包管理器。为了简化这个过程,Node.js 提供了一个名为 Corepack 的工具,它旨在让所有包管理器在你的环境中透明可用——前提是你已经安装了 Node.js。
【While all Node.js projects are expected to be installable by all package managers once published, their development teams are often required to use one specific package manager. To make this process easier, Node.js ships with a tool called Corepack that aims to make all package managers transparently available in your environment - provided you have Node.js installed.】
默认情况下,Corepack 不会强制使用任何特定的包管理器,而是使用与每个 Node.js 版本关联的通用“最后已知良好”版本,但你可以通过在项目的 package.json 中设置 "packageManager" 字段来改善这一体验。
【By default Corepack won't enforce any specific package manager and will use
the generic "Last Known Good" versions associated with each Node.js release,
but you can improve this experience by setting the "packageManager" field
in your project's package.json.】
包入口点#>
【Package entry points】
在一个包的 package.json 文件中,有两个字段可以定义包的入口点:"main" 和 "exports"。两个字段都适用于 ES 模块和 CommonJS 模块的入口点。
【In a package's package.json file, two fields can define entry points for a
package: "main" and "exports". Both fields apply to both ES module
and CommonJS module entry points.】
"main" 字段在所有版本的 Node.js 中都受到支持,但其功能有限:它仅定义了包的主要入口点。
【The "main" field is supported in all versions of Node.js, but its
capabilities are limited: it only defines the main entry point of the package.】
"exports" 提供了 "main" 的现代替代方案,允许定义多个入口点,支持在不同环境之间进行条件入口解析,并且防止使用除 "exports" 中定义的入口点之外的任何其他入口点。这种封装使模块作者能够清晰地定义他们包的公共接口。
【The "exports" provides a modern alternative to "main" allowing
multiple entry points to be defined, conditional entry resolution support
between environments, and preventing any other entry points besides those
defined in "exports". This encapsulation allows module authors to
clearly define the public interface for their package.】
对于面向当前支持的 Node.js 版本的新软件包,建议使用 "exports" 字段。对于支持 Node.js 10 及以下版本的软件包,必须使用 "main" 字段。如果同时定义了 "exports" 和 "main" 字段,则在支持的 Node.js 版本中,"exports" 字段优先于 "main" 字段。
【For new packages targeting the currently supported versions of Node.js, the
"exports" field is recommended. For packages supporting Node.js 10 and
below, the "main" field is required. If both "exports" and
"main" are defined, the "exports" field takes precedence over
"main" in supported versions of Node.js.】
条件导出 可以在 "exports" 中使用,以定义每个环境的不同包入口点,包括该包是通过 require 还是通过 import 引用。有关在单个包中同时支持 CommonJS 和 ES 模块的更多信息,请参阅 双重 CommonJS/ES 模块包部分。
引入 "exports" 字段的现有包将阻止包的使用者使用任何未定义的入口点,包括 package.json(例如 require('your-package/package.json'))。这很可能会是一个重大更改。
【Existing packages introducing the "exports" field will prevent consumers
of the package from using any entry points that are not defined, including the
package.json (e.g. require('your-package/package.json'). This will
likely be a breaking change.】
为了使 "exports" 的引入保持向后兼容,请确保导出之前支持的每个入口点。最好明确指定入口点,以便包的公共 API 定义清晰。例如,一个之前导出 main、lib、feature 和 package.json 的项目可以使用以下 package.exports:
【To make the introduction of "exports" non-breaking, ensure that every
previously supported entry point is exported. It is best to explicitly specify
entry points so that the package's public API is well-defined. For example,
a project that previously exported main, lib,
feature, and the package.json could use the following package.exports:】
{
"name": "my-package",
"exports": {
".": "./lib/index.js",
"./lib": "./lib/index.js",
"./lib/index": "./lib/index.js",
"./lib/index.js": "./lib/index.js",
"./feature": "./feature/index.js",
"./feature/index": "./feature/index.js",
"./feature/index.js": "./feature/index.js",
"./package.json": "./package.json"
}
}
或者,一个项目可以选择使用导出模式导出整个文件夹,同时包含或不包含带扩展名的子路径:
【Alternatively a project could choose to export entire folders both with and without extensioned subpaths using export patterns:】
{
"name": "my-package",
"exports": {
".": "./lib/index.js",
"./lib": "./lib/index.js",
"./lib/*": "./lib/*.js",
"./lib/*.js": "./lib/*.js",
"./feature": "./feature/index.js",
"./feature/*": "./feature/*.js",
"./feature/*.js": "./feature/*.js",
"./package.json": "./package.json"
}
}
通过上述为任何次要版本提供向后兼容性,包的未来重大更改就可以适当地将导出限制为仅公开的特定功能导出:
【With the above providing backwards-compatibility for any minor package versions, a future major change for the package can then properly restrict the exports to only the specific feature exports exposed:】
{
"name": "my-package",
"exports": {
".": "./lib/index.js",
"./feature/*.js": "./feature/*.js",
"./feature/internal/*": null
}
}
主入口点导出#>
【Main entry point export】
在编写新软件包时,建议使用 "exports" 字段:
【When writing a new package, it is recommended to use the "exports" field:】
{
"exports": "./index.js"
}
当定义了 "exports" 字段时,包的所有子路径都会被封装,导入者将无法再访问。例如,require('pkg/subpath.js') 会抛出 ERR_PACKAGE_PATH_NOT_EXPORTED 错误。
【When the "exports" field is defined, all subpaths of the package are
encapsulated and no longer available to importers. For example,
require('pkg/subpath.js') throws an ERR_PACKAGE_PATH_NOT_EXPORTED
error.】
这种对导出的封装为工具以及在处理包的语义化版本升级时提供了更可靠的包接口保证。这不是一种强封装,因为对包的任何绝对子路径的直接 require,例如 require('/path/to/node_modules/pkg/subpath.js'),仍然会加载 subpath.js。
【This encapsulation of exports provides more reliable guarantees
about package interfaces for tools and when handling semver upgrades for a
package. It is not a strong encapsulation since a direct require of any
absolute subpath of the package such as
require('/path/to/node_modules/pkg/subpath.js') will still load subpath.js.】
所有目前支持的 Node.js 版本和现代构建工具都支持 "exports" 字段。对于使用较旧版本 Node.js 或相关构建工具的项目,可以通过在 "exports" 字段旁边包含 "main" 字段并指向同一个模块来实现兼容性:
【All currently supported versions of Node.js and modern build tools support the
"exports" field. For projects using an older version of Node.js or a related
build tool, compatibility can be achieved by including the "main" field
alongside "exports" pointing to the same module:】
{
"main": "./index.js",
"exports": "./index.js"
}
子路径导出#>
【Subpath exports】
在使用 "exports" 字段时,可以定义自定义子路径以及主入口点,通过将主入口点视为 "." 子路径来实现:
【When using the "exports" field, custom subpaths can be defined along
with the main entry point by treating the main entry point as the
"." subpath:】
{
"exports": {
".": "./index.js",
"./submodule.js": "./src/submodule.js"
}
}
现在只有在 "exports" 中定义的子路径可以被使用者导入:
【Now only the defined subpath in "exports" can be imported by a consumer:】
import submodule from 'es-module-package/submodule.js';
// Loads ./node_modules/es-module-package/src/submodule.js
而其他子路径会出错:
【While other subpaths will error:】
import submodule from 'es-module-package/private-module.js';
// Throws ERR_PACKAGE_PATH_NOT_EXPORTED
子路径中的扩展#>
【Extensions in subpaths】
包的作者应该在其导出中提供带扩展名(import 'pkg/subpath.js')或不带扩展名(import 'pkg/subpath')的子路径。这确保每个导出模块只有一个子路径,从而使所有依赖方导入相同的一致的标识符,保持包的契约对使用者清晰,并简化包子路径的补全。
【Package authors should provide either extensioned (import 'pkg/subpath.js') or
extensionless (import 'pkg/subpath') subpaths in their exports. This ensures
that there is only one subpath for each exported module so that all dependents
import the same consistent specifier, keeping the package contract clear for
consumers and simplifying package subpath completions.】
传统上,包通常倾向于使用无扩展名的风格,这种风格具有可读性强以及隐藏包内文件真实路径的优点。
【Traditionally, packages tended to use the extensionless style, which has the benefits of readability and of masking the true path of the file within the package.】
随着 导入地图 现在为浏览器和其他 JavaScript 运行时提供了包解析的标准,使用无扩展名的风格可能会导致导入映射定义膨胀。显式的文件扩展名可以避免这个问题,因为它使导入映射能够利用 包文件夹映射 尽可能地映射多个子路径,而不必为每个包子路径导出创建单独的映射条目。这也反映了在相对和绝对导入说明符中使用 完整限定路径 的要求。
【With import maps now providing a standard for package resolution in browsers and other JavaScript runtimes, using the extensionless style can result in bloated import map definitions. Explicit file extensions can avoid this issue by enabling the import map to utilize a packages folder mapping to map multiple subpaths where possible instead of a separate map entry per package subpath export. This also mirrors the requirement of using the full specifier path in relative and absolute import specifiers.】
导出糖#>
【Exports sugar】
如果是'“。”出口是唯一的出口,"exports"田提供糖
对于此情况,则是直接的"exports"场值。
【If the "." export is the only export, the "exports" field provides sugar
for this case being the direct "exports" field value.】
{
"exports": {
".": "./index.js"
}
}
可以写成:
【can be written:】
{
"exports": "./index.js"
}
子路径导入#>
【Subpath imports】
除了"exports"字段外,还有一个包“导入”字段
创建仅适用于从
封装本身。
【In addition to the "exports" field, there is a package "imports" field
to create private mappings that only apply to import specifiers from within the
package itself.】
"imports" 字段中的条目必须始终以 # 开头,以确保它们与外部包说明符区分开来。
【Entries in the "imports" field must always start with # to ensure they are
disambiguated from external package specifiers.】
例如,imports 字段可以用于为内部模块获得条件导出的好处:
【For example, the imports field can be used to gain the benefits of conditional exports for internal modules:】
// package.json
{
"imports": {
"#dep": {
"node": "dep-node-native",
"default": "./dep-polyfill.js"
}
},
"dependencies": {
"dep-node-native": "^1.0.0"
}
}
当 import '#dep' 无法解析外部包 dep-node-native(包括其导出内容)时,它会在其他环境中获取相对于该包的本地文件 ./dep-polyfill.js。
【where import '#dep' does not get the resolution of the external package
dep-node-native (including its exports in turn), and instead gets the local
file ./dep-polyfill.js relative to the package in other environments.】
与 "exports" 字段不同,"imports" 字段允许映射到外部包。
【Unlike the "exports" field, the "imports" field permits mapping to external
packages.】
imports 字段的解析规则在其他方面与 exports 字段类似。
【The resolution rules for the imports field are otherwise analogous to the exports field.】
子路径模式#>
【Subpath patterns】
对于导出或导入数量较少的包,我们建议显式列出每个 exports 子路径条目。但对于子路径数量较多的包,这可能会导致 package.json 文件变大并带来维护问题。
【For packages with a small number of exports or imports, we recommend
explicitly listing each exports subpath entry. But for packages that have
large numbers of subpaths, this might cause package.json bloat and
maintenance issues.】
对于这些用例,可以使用子路径导出模式:
【For these use cases, subpath export patterns can be used instead:】
// ./node_modules/es-module-package/package.json
{
"exports": {
"./features/*.js": "./src/features/*.js"
},
"imports": {
"#internal/*.js": "./src/internal/*.js"
}
}
* 映射仅作为字符串替换语法暴露嵌套子路径。
右边所有的 * 实例都将被此值替换,包括其中包含任何 / 分隔符的情况。
【All instances of * on the right hand side will then be replaced with this
value, including if it contains any / separators.】
import featureX from 'es-module-package/features/x.js';
// Loads ./node_modules/es-module-package/src/features/x.js
import featureY from 'es-module-package/features/y/y.js';
// Loads ./node_modules/es-module-package/src/features/y/y.js
import internalZ from '#internal/z.js';
// Loads ./node_modules/es-module-package/src/internal/z.js
这是一个直接的静态匹配和替换,不对文件扩展名进行任何特殊处理。在映射的两侧都包含 "*.js" 会将暴露的包导出限制为仅 JS 文件。
【This is a direct static matching and replacement without any special handling
for file extensions. Including the "*.js" on both sides of the mapping
restricts the exposed package exports to only JS files.】
通过导出模式,导出具有静态可枚举性的特性得以保持,因为可以通过将右侧的目标模式视为针对包内文件列表的 ** 通配符来确定包的各个导出。由于在导出目标中禁止使用 node_modules 路径,这种展开仅依赖于包自身的文件。
【The property of exports being statically enumerable is maintained with exports
patterns since the individual exports for a package can be determined by
treating the right hand side target pattern as a ** glob against the list of
files within the package. Because node_modules paths are forbidden in exports
targets, this expansion is dependent on only the files of the package itself.】
要从模式中排除私有子文件夹,可以使用 null 目标:
【To exclude private subfolders from patterns, null targets can be used:】
// ./node_modules/es-module-package/package.json
{
"exports": {
"./features/*.js": "./src/features/*.js",
"./features/private-internal/*": null
}
}
import featureInternal from 'es-module-package/features/private-internal/m.js';
// Throws: ERR_PACKAGE_PATH_NOT_EXPORTED
import featureX from 'es-module-package/features/x.js';
// Loads ./node_modules/es-module-package/src/features/x.js
条件导出#>
【Conditional exports】
条件导出提供了一种根据特定条件映射到不同路径的方式。它们同时支持 CommonJS 和 ES 模块导入。
【Conditional exports provide a way to map to different paths depending on certain conditions. They are supported for both CommonJS and ES module imports.】
例如,一个希望为 require() 和 import 提供不同 ES 模块导出的包可以这样编写:
【For example, a package that wants to provide different ES module exports for
require() and import can be written:】
// package.json
{
"exports": {
"import": "./index-module.js",
"require": "./index-require.cjs"
},
"type": "module"
}
Node.js 实现了以下条件,按从最具体到最不具体的顺序列出,因为条件应如此定义:
【Node.js implements the following conditions, listed in order from most specific to least specific as conditions should be defined:】
"node-addons"- 类似于"node",适用于任何 Node.js 环境。该条件可用于提供使用本地 C++ 插件的入口点,而不是更通用且不依赖本地插件的入口点。此条件可以通过--no-addons标志 禁用。node- 匹配任何 Node.js 环境。可以是 CommonJS 或 ES 模块文件。在大多数情况下,不需要明确指定 Node.js 平台。"import"- 当通过import或import()加载包,或通过 ECMAScript 模块加载器的任何顶层导入或解析操作加载包时匹配。无论目标文件的模块格式如何都适用。始终与"require"互斥."require"- 当通过require()加载包时匹配。被引用的文件应可以通过require()加载,尽管无论目标文件的模块格式如何,此条件都会匹配。预期格式包括 CommonJS、JSON 和本地插件,但不包括 ES 模块,因为require()不支持它们。始终与"import"互斥。"default"- 通用的回退选项,总是匹配。可以是 CommonJS 或 ES 模块文件。此条件应始终放在最后。
在 "exports" 对象中,键的顺序是重要的。在条件匹配过程中,较早的条目具有更高的优先级,优先于较晚的条目。一般规则是,条件应按照从最具体到最不具体的顺序排列在对象中。
【Within the "exports" object, key order is significant. During condition
matching, earlier entries have higher priority and take precedence over later
entries. The general rule is that conditions should be from most specific to
least specific in object order.】
使用 "import" 和 "require" 条件可能会导致一些风险,这些风险在 双重 CommonJS/ES 模块包部分 中有进一步解释。
【Using the "import" and "require" conditions can lead to some hazards,
which are further explained in the dual CommonJS/ES module packages section.】
"node-addons" 条件可以用来提供一个使用本地 C++ 插件的入口点。然而,该条件可以通过 --no-addons 标志 被禁用。在使用 "node-addons" 时,建议将 "default" 视为一种增强,提供一个更通用的入口点,例如使用 WebAssembly 代替本地插件。
【The "node-addons" condition can be used to provide an entry point which
uses native C++ addons. However, this condition can be disabled via the
--no-addons flag. When using "node-addons", it's recommended to treat
"default" as an enhancement that provides a more universal entry point, e.g.
using WebAssembly instead of a native addon.】
条件导出也可以扩展为导出子路径,例如:
【Conditional exports can also be extended to exports subpaths, for example:】
{
"exports": {
".": "./index.js",
"./feature.js": {
"node": "./feature-node.js",
"default": "./feature.js"
}
}
}
定义一个包,在该包中 require('pkg/feature.js') 和 import 'pkg/feature.js' 在 Node.js 与其他 JS 环境之间可能提供不同的实现。
【Defines a package where require('pkg/feature.js') and
import 'pkg/feature.js' could provide different implementations between
Node.js and other JS environments.】
在使用环境分支时,尽可能始终包含一个 "default" 条件。提供 "default" 条件可以确保任何未知的 JS 环境能够使用这个通用实现,这有助于避免这些 JS 环境为了支持具有条件导出的包而不得不假装成现有环境。因此,使用 "node" 和 "default" 条件分支通常比使用 "node" 和 "browser" 条件分支更可取。
【When using environment branches, always include a "default" condition where
possible. Providing a "default" condition ensures that any unknown JS
environments are able to use this universal implementation, which helps avoid
these JS environments from having to pretend to be existing environments in
order to support packages with conditional exports. For this reason, using
"node" and "default" condition branches is usually preferable to using
"node" and "browser" condition branches.】
嵌套条件#>
【Nested conditions】
除了直接映射,Node.js 还支持嵌套条件对象。
【In addition to direct mappings, Node.js also supports nested condition objects.】
例如,要定义一个只在 Node.js 中使用而不在浏览器中使用的具有双模式入口点的包:
【For example, to define a package that only has dual mode entry points for use in Node.js but not the browser:】
{
"exports": {
"node": {
"import": "./feature-node.mjs",
"require": "./feature-node.cjs"
},
"default": "./feature.mjs"
}
}
条件会按顺序继续匹配,就像平铺的条件一样。如果嵌套条件没有任何映射,它将继续检查父条件的其余条件。通过这种方式,嵌套条件的行为类似于嵌套的 JavaScript if 语句。
【Conditions continue to be matched in order as with flat conditions. If
a nested condition does not have any mapping it will continue checking
the remaining conditions of the parent condition. In this way nested
conditions behave analogously to nested JavaScript if statements.】
解析用户条件#>
【Resolving user conditions】
在运行 Node.js 时,可以使用 --conditions 标志添加自定义用户条件:
【When running Node.js, custom user conditions can be added with the
--conditions flag:】
node --conditions=development index.js
这将会解决包导入和导出中的“development”条件,同时适当地解决现有的“node”、“node-addons”、“default”、“import”和“require”条件。
【which would then resolve the "development" condition in package imports and
exports, while resolving the existing "node", "node-addons", "default",
"import", and "require" conditions as appropriate.】
可以使用重复标志设置任意数量的自定义条件。
【Any number of custom conditions can be set with repeat flags.】
社区条件定义#>
【Community Conditions Definitions】
除 "import"、"require"、"node"、"node-addons" 和 "default" 条件外的条件字符串 在 Node.js 核心中实现 默认会被忽略。
【Condition strings other than the "import", "require", "node",
"node-addons" and "default" conditions
implemented in Node.js core are ignored by default.】
其他平台可能会实现其他条件,并且可以通过 --conditions / -C 标志 在 Node.js 中启用用户条件。
【Other platforms may implement other conditions and user conditions can be
enabled in Node.js via the --conditions / -C flag.】
由于定制包的条件需要明确的定义以确保正确使用,以下提供了一份常见已知包条件及其严格定义的列表,以协助生态系统协调。
【Since custom package conditions require clear definitions to ensure correct usage, a list of common known package conditions and their strict definitions is provided below to assist with ecosystem coordination.】
"types"- 可以被类型系统用于解析给定导出的类型文件。此条件应始终首先包含。"deno"- 表示用于 Deno 平台的一个变体。"browser"- 任何网页浏览器环境。"development"- 可用于定义仅限开发的环境入口,例如在开发模式下提供额外的调试信息,如更好的错误提示。必须始终与"production"互斥。"production"- 可用于定义生产环境的入口点。必须始终与"development"互斥。
可以通过向 本节的 Node.js 文档 创建拉取请求将新的条件定义添加到此列表中。在此列出新条件定义的要求是:
【New conditions definitions may be added to this list by creating a pull request to the Node.js documentation for this section. The requirements for listing a new condition definition here are that:】
- 定义应当清晰明确,以便所有实现者都能理解。
- 需要明确说明为什么需要该条件的使用情况。
- 应该存在足够的现有实现使用。
- 条件名称不应与其他条件定义或广泛使用的条件冲突。
- 条件定义的列表应该为生态系统提供一种协调效益,而这是在其他情况下无法实现的。例如,公司特定或应用特定的条件不一定能够实现这种效果。
上述定义可能会在适当的时候移至专门的条件登记处。
【The above definitions may be moved to a dedicated conditions registry in due course.】
使用名称自引用包#>
【Self-referencing a package using its name】
在一个包中,可以通过包的名称引用在包的 package.json 的 "exports" 字段中定义的值。例如,假设 package.json 如下:
【Within a package, the values defined in the package's
package.json "exports" field can be referenced via the package's name.
For example, assuming the package.json is:】
// package.json
{
"name": "a-package",
"exports": {
".": "./index.mjs",
"./foo.js": "./foo.js"
}
}
那么该包中的任何模块都可以引用包本身中的导出内容:
【Then any module in that package can reference an export in the package itself:】
// ./a-module.mjs
import { something } from 'a-package'; // Imports "something" from ./index.mjs.
只有在 package.json 中有 "exports" 时才可以使用自引用,并且只允许导入该 "exports"(在 package.json 中)允许的内容。因此,以下代码在使用之前的包时,将会产生运行时错误:
【Self-referencing is available only if package.json has "exports", and
will allow importing only what that "exports" (in the package.json)
allows. So the code below, given the previous package, will generate a runtime
error:】
// ./another-module.mjs
// Imports "another" from ./m.mjs. Fails because
// the "package.json" "exports" field
// does not provide an export named "./m.mjs".
import { another } from 'a-package/m.mjs';
在使用 require 时,也可以进行自我引用,无论是在 ES 模块中,还是在 CommonJS 模块中。例如,这段代码也可以正常运行:
【Self-referencing is also available when using require, both in an ES module,
and in a CommonJS one. For example, this code will also work:】
// ./a-module.js
const { something } = require('a-package/foo.js'); // Loads from ./foo.js.
最后,自引用也适用于有作用域的包。例如,这段代码也可以正常工作:
【Finally, self-referencing also works with scoped packages. For example, this code will also work:】
// package.json
{
"name": "@my/package",
"exports": "./index.js"
}
// ./index.js
module.exports = 42;
// ./other.js
console.log(require('@my/package'));
$ node other.js
42
子路径文件夹映射#>
【Subpath folder mappings】
在子路径模式支持之前,后尾的“/”后缀被用于 支持文件夹映射:
【Before subpath patterns were supported, a trailing "/" suffix was used to
support folder mappings:】
{
"exports": {
"./features/": "./features/"
}
}
此功能将在未来版本中被移除.
【This feature will be removed in a future release.】
相反,使用直接 子路径模式:
【Instead, use direct subpath patterns:】
{
"exports": {
"./features/*": "./features/*.js"
}
}
相较于文件夹导出,使用模式的好处是,消费者总是可以导入包,而不需要子路径文件扩展名。
【The benefit of patterns over folder exports is that packages can always be imported by consumers without subpath file extensions being necessary.】
双 CommonJS/ES 模块包#>
【Dual CommonJS/ES module packages】
在 Node.js 引入对 ES 模块支持之前,包作者通常会在他们的包中同时包含 CommonJS 和 ES 模块的 JavaScript 源文件,并在 package.json 中通过 "main" 指定 CommonJS 入口点,通过 package.json 中的 "module" 指定 ES 模块入口点。这使得 Node.js 可以运行 CommonJS 入口点,而构建工具(如打包器)则使用 ES 模块入口点,因为 Node.js 会忽略(并且仍然忽略)顶层的 "module" 字段。
【Prior to the introduction of support for ES modules in Node.js, it was a common
pattern for package authors to include both CommonJS and ES module JavaScript
sources in their package, with package.json "main" specifying the
CommonJS entry point and package.json "module" specifying the ES module
entry point.
This enabled Node.js to run the CommonJS entry point while build tools such as
bundlers used the ES module entry point, since Node.js ignored (and still
ignores) the top-level "module" field.】
Node.js现在可以运行ES模块的入口点,一个包可以同时包含两者 CommonJS 和 ES 模块的入口点(通过不同的指定符,如 “pkg”和“pkg/es-module”,或通过条件导出在同一指定符下两者。与“模”仅供打包者使用的情景不同, 或者在评估前,ES模块文件会在被 Node.js,ES模块入口点引用的文件被评估为ES 模块。
【Node.js can now run ES module entry points, and a package can contain both
CommonJS and ES module entry points (either via separate specifiers such as
'pkg' and 'pkg/es-module', or both at the same specifier via Conditional
exports). Unlike in the scenario where "module" is only used by bundlers,
or ES module files are transpiled into CommonJS on the fly before evaluation by
Node.js, the files referenced by the ES module entry point are evaluated as ES
modules.】
双封装危害#>
【Dual package hazard】
当一个应用使用同时提供 CommonJS 和 ES 模块来源的包时,如果两个版本的包都被加载,就存在某些错误的风险。这种潜在问题源于通过 const pkgInstance = require('pkg') 创建的 pkgInstance 与通过 import pkgInstance from 'pkg'(或类似的主路径如 'pkg/module')创建的 pkgInstance 不同。这就是所谓的“混合包风险”,即同一个运行环境中可能加载两个版本的相同包。虽然应用或包不太可能故意直接加载这两个版本,但应用加载一个版本而其依赖加载另一个版本的情况很常见。由于 Node.js 支持混合使用 CommonJS 和 ES 模块,这种风险可能导致意外行为。
【When an application is using a package that provides both CommonJS and ES module
sources, there is a risk of certain bugs if both versions of the package get
loaded. This potential comes from the fact that the pkgInstance created by
const pkgInstance = require('pkg') is not the same as the pkgInstance
created by import pkgInstance from 'pkg' (or an alternative main path like
'pkg/module'). This is the “dual package hazard,” where two versions of the
same package can be loaded within the same runtime environment. While it is
unlikely that an application or package would intentionally load both versions
directly, it is common for an application to load one version while a dependency
of the application loads the other version. This hazard can happen because
Node.js supports intermixing CommonJS and ES modules, and can lead to unexpected
behavior.】
如果包 main 的导出是一个构造函数,通过两个版本创建的实例进行 instanceof 比较会返回 false,如果导出的是一个对象,则在一个对象上添加的属性(如 pkgInstance.foo = 3)在另一个对象上不会存在。这与在全 CommonJS 或全 ES 模块环境中 import 和 require 语句的工作方式不同,因此会让用户感到意外。这也不同于用户在使用像 Babel 或 esm 这样的工具进行转译时所熟悉的行为。
【If the package main export is a constructor, an instanceof comparison of
instances created by the two versions returns false, and if the export is an
object, properties added to one (like pkgInstance.foo = 3) are not present on
the other. This differs from how import and require statements work in
all-CommonJS or all-ES module environments, respectively, and therefore is
surprising to users. It also differs from the behavior users are familiar with
when using transpilation via tools like Babel or esm.】
在避免或最小化危险的同时编写双重包#>
【Writing dual packages while avoiding or minimizing hazards】
首先,上一节中描述的风险发生在一个包同时包含 CommonJS 和 ES 模块源文件,并且这两种源文件都可用于 Node.js,无论是通过单独的主入口点还是导出的路径提供的情况下。一个包也可能被编写为任何版本的 Node.js 仅接收 CommonJS 源文件,而包中可能包含的任何单独的 ES 模块源文件仅用于诸如浏览器等其他环境。这样的包可以被任何版本的 Node.js 使用,因为 import 可以引用 CommonJS 文件;但它不会提供使用 ES 模块语法的任何优势。
【First, the hazard described in the previous section occurs when a package
contains both CommonJS and ES module sources and both sources are provided for
use in Node.js, either via separate main entry points or exported paths. A
package might instead be written where any version of Node.js receives only
CommonJS sources, and any separate ES module sources the package might contain
are intended only for other environments such as browsers. Such a package
would be usable by any version of Node.js, since import can refer to CommonJS
files; but it would not provide any of the advantages of using ES module syntax.】
一个包也可能在重大更改版本升级中从 CommonJS 切换到 ES 模块语法。这有一个缺点,即包的最新版本只能在支持 ES 模块的 Node.js 版本中使用。
【A package might also switch from CommonJS to ES module syntax in a breaking change version bump. This has the disadvantage that the newest version of the package would only be usable in ES module-supporting versions of Node.js.】
每种模式都有利弊,但有两种广泛的方法可以满足以下条件:
【Every pattern has tradeoffs, but there are two broad approaches that satisfy the following conditions:】
- 该包可以通过
require和import使用。 - 该软件包可以在当前版本的 Node.js 以及缺乏 ES 模块支持的旧版本 Node.js 中使用。
- 包的主入口,例如
'pkg',可以被require用于解析为 CommonJS 文件,也可以被import用于解析为 ES 模块文件。(导出的路径同理,例如'pkg/feature'。) - 该包提供具名导出,例如
import { name } from 'pkg',而不是import pkg from 'pkg'; pkg.name。 - 该软件包可能在其他 ES 模块环境中使用,例如浏览器。
- 前一节中描述的危险已被避免或最小化。
方法 #1:使用 ES 模块封装器#>
【Approach #1: Use an ES module wrapper】
将包写成 CommonJS,或将 ES 模块源代码转译为 CommonJS,并创建一个定义命名导出的 ES 模块封装文件。使用 条件导出 时,ES 模块封装文件用于 import,CommonJS 入口点用于 require。
【Write the package in CommonJS or transpile ES module sources into CommonJS, and
create an ES module wrapper file that defines the named exports. Using
Conditional exports, the ES module wrapper is used for import and the
CommonJS entry point for require.】
// ./node_modules/pkg/package.json
{
"type": "module",
"exports": {
"import": "./wrapper.mjs",
"require": "./index.cjs"
}
}
前面的示例使用了显式扩展名 .mjs 和 .cjs。如果你的文件使用 .js 扩展名,"type": "module" 将导致这些文件被视为 ES 模块,就像 "type": "commonjs" 会导致它们被视为 CommonJS 模块一样。请参阅 启用。
【The preceding example uses explicit extensions .mjs and .cjs.
If your files use the .js extension, "type": "module" will cause such files
to be treated as ES modules, just as "type": "commonjs" would cause them
to be treated as CommonJS.
See Enabling.】
// ./node_modules/pkg/index.cjs
exports.name = 'value';
// ./node_modules/pkg/wrapper.mjs
import cjsModule from './index.cjs';
export const name = cjsModule.name;
在这个例子中,来自 import { name } from 'pkg' 的 name 与来自 const { name } = require('pkg') 的 name 是同一个单例。因此,在比较两个 name 时,=== 返回 true,并且避免了不同的导入说明符风险。
【In this example, the name from import { name } from 'pkg' is the same
singleton as the name from const { name } = require('pkg'). Therefore ===
returns true when comparing the two names and the divergent specifier hazard
is avoided.】
如果模块不仅仅是一个命名导出列表,而是包含一个独特的函数或对象导出,比如 module.exports = function () { ... },或者如果希望封装器支持 import pkg from 'pkg' 这种模式,那么封装器将会被改写为可选择性地导出默认导出,同时也可以导出任何命名导出:
【If the module is not simply a list of named exports, but rather contains a
unique function or object export like module.exports = function () { ... },
or if support in the wrapper for the import pkg from 'pkg' pattern is desired,
then the wrapper would instead be written to export the default optionally
along with any named exports as well:】
import cjsModule from './index.cjs';
export const name = cjsModule.name;
export default cjsModule;
此方法适用于以下任何用例:
【This approach is appropriate for any of the following use cases:】
- 该包目前是用 CommonJS 编写的,作者不希望将其重构为 ES 模块语法,但希望为 ES 模块使用者提供命名导出。
- 该软件包有其他依赖于它的软件包,终端用户可能会同时安装此软件包和那些其他软件包。例如,一个
utilities软件包直接在应用中使用,而utilities-plus软件包则为utilities添加了更多功能。由于封装器导出了底层的 CommonJS 文件,所以无论utilities-plus是使用 CommonJS 语法还是 ES 模块语法编写的,都可以正常工作。 - 该包存储内部状态,并且包的作者不希望重构该包以隔离其状态管理。请参见下一节。
这种方法的一种变体是不需要为使用者设置条件导出,可以添加一个导出,例如 "./module",指向该包的全 ES 模块语法版本。对于那些确定应用中不会加载 CommonJS 版本的用户(例如通过依赖),或即使加载了 CommonJS 版本也不会影响 ES 模块版本的情况(例如因为该包是无状态的),可以通过 import 'pkg/module' 使用该导出:
【A variant of this approach not requiring conditional exports for consumers could
be to add an export, e.g. "./module", to point to an all-ES module-syntax
version of the package. This could be used via import 'pkg/module' by users
who are certain that the CommonJS version will not be loaded anywhere in the
application, such as by dependencies; or if the CommonJS version can be loaded
but doesn't affect the ES module version (for example, because the package is
stateless):】
// ./node_modules/pkg/package.json
{
"type": "module",
"exports": {
".": "./index.cjs",
"./module": "./wrapper.mjs"
}
}
方法二:隔离状态#>
【Approach #2: Isolate state】
package.json 文件可以直接定义独立的 CommonJS 和 ES 模块入口点:
【A package.json file can define the separate CommonJS and ES module entry
points directly:】
// ./node_modules/pkg/package.json
{
"type": "module",
"exports": {
"import": "./index.mjs",
"require": "./index.cjs"
}
}
如果软件包的 CommonJS 版本和 ES 模块版本是等效的,例如因为其中一个是另一个的转译输出;并且软件包的状态管理被仔细隔离(或者软件包是无状态的),那么这是可行的。
【This can be done if both the CommonJS and ES module versions of the package are equivalent, for example because one is the transpiled output of the other; and the package's management of state is carefully isolated (or the package is stateless).】
之所以状态会成为问题,是因为一个应用可能同时使用该包的 CommonJS 和 ES 模块版本;例如,用户的应用代码可能会 import ES 模块版本,而某个依赖可能会 require CommonJS 版本。如果发生这种情况,该包将会被加载两次到内存中,因此会存在两个独立的状态。这很可能会导致难以排查的错误。
【The reason that state is an issue is because both the CommonJS and ES module
versions of the package might get used within an application; for example, the
user's application code could import the ES module version while a dependency
requires the CommonJS version. If that were to occur, two copies of the
package would be loaded in memory and therefore two separate states would be
present. This would likely cause hard-to-troubleshoot bugs.】
除了编写无状态的包(例如,如果 JavaScript 的 Math 是一个包,它将是无状态的,因为它的所有方法都是静态的),还有一些方法可以隔离状态,以便在包的可能加载的 CommonJS 和 ES 模块实例之间共享状态:
【Aside from writing a stateless package (if JavaScript's Math were a package,
for example, it would be stateless as all of its methods are static), there are
some ways to isolate state so that it's shared between the potentially loaded
CommonJS and ES module instances of the package:】
-
如果可能的话,将所有状态包含在实例化对象中。例如,JavaScript 的
Date需要被实例化才能包含状态;如果它是一个包,它的使用方式如下:import Date from 'date'; const someDate = new Date(); // someDate contains state; Date does notnew关键字不是必须的;一个包的函数可以返回一个新对象,或者修改传入的对象,以保持状态在包外部。 -
将状态隔离到一个或多个在 CommonJS 和 ES 模块版本的包之间共享的 CommonJS 文件中。例如,如果 CommonJS 和 ES 模块的入口点分别是
index.cjs和index.mjs:// ./node_modules/pkg/index.cjs const state = require('./state.cjs'); module.exports.state = state;// ./node_modules/pkg/index.mjs import state from './state.cjs'; export { state };即使在应用中既通过
require又通过import使用pkg(例如,在应用代码中通过import,而在依赖中通过require),每个对pkg的引用都将包含相同的状态;并且从任一模块系统修改该状态都会对两者生效。
任何附加到该包单例的插件都需要分别附加到 CommonJS 和 ES 模块单例上。
【Any plugins that attach to the package's singleton would need to separately attach to both the CommonJS and ES module singletons.】
此方法适用于以下任何用例:
【This approach is appropriate for any of the following use cases:】
- 该软件包目前是用 ES 模块语法编写的,软件包作者希望在支持这种语法的地方使用该版本。
- 该软件包是无状态的,或者其状态可以在不太困难的情况下被隔离。
- 该包不太可能有其他依赖于它的公共包,或者即使有,该包也是无状态的,或者其状态不需要在依赖之间或与整个应用共享。
即使在隔离状态下,仍然存在在包的 CommonJS 版本和 ES 模块版本之间可能额外执行代码的成本。
【Even with isolated state, there is still the cost of possible extra code execution between the CommonJS and ES module versions of a package.】
与之前的方法一样,这种方法的一个变体是不需要为使用者提供条件导出的,可以添加一个导出,例如 "./module",指向该包的全 ES 模块语法版本:
【As with the previous approach, a variant of this approach not requiring
conditional exports for consumers could be to add an export, e.g.
"./module", to point to an all-ES module-syntax version of the package:】
// ./node_modules/pkg/package.json
{
"type": "module",
"exports": {
".": "./index.cjs",
"./module": "./index.mjs"
}
}
Node.js package.json 字段定义#>
【Node.js package.json field definitions】
本节描述了 Node.js 运行时使用的字段。其他工具(例如 npm)使用额外的字段,这些字段在 Node.js 中会被忽略,并且在此处未作记录。
【This section describes the fields used by the Node.js runtime. Other tools (such as npm) use additional fields which are ignored by Node.js and not documented here.】
package.json 文件中的以下字段在 Node.js 中使用:
【The following fields in package.json files are used in Node.js:】
"name"- 在包中使用具名导入时相关。也被包管理器用作包的名称。"main"- 加载包时的默认模块,如果未指定 exports,并且在 Node.js 引入 exports 之前的版本中。"packageManager"- 在为该软件包贡献时推荐的包管理器。由 Corepack 插件使用。"type"- 包类型决定是否将.js文件作为 CommonJS 或 ES 模块加载。"exports"- 包导出和条件导出。当存在时,限制可以从包内部加载的子模块。"imports"- 包导入,供包内模块自身使用。
"name"#>
- 类型:<string>
{
"name": "package-name"
}
"name" 字段定义了你的包的名称。发布到 npm 注册表需要一个满足 某些要求 的名称。
【The "name" field defines your package's name. Publishing to the
npm registry requires a name that satisfies
certain requirements.】
"name" 字段可以与 "exports" 字段一起使用,通过包名来 自我指涉 一个包。
【The "name" field can be used in addition to the "exports" field to
self-reference a package using its name.】
"main"#>
- 类型:<string>
{
"main": "./index.js"
}
"main" 字段定义了通过 node_modules 查找按名称导入时包的入口点。它的值是一个路径。
【The "main" field defines the entry point of a package when imported by name
via a node_modules lookup. Its value is a path.】
当一个包有 "exports" 字段时,在按名称导入该包时,这将优先于 "main" 字段。
【When a package has an "exports" field, this will take precedence over the
"main" field when importing the package by name.】
它还定义了在通过 require() 加载包目录时使用的脚本。
【It also defines the script that is used when the package directory is loaded
via require().】
// This resolves to ./path/to/directory/index.js.
require('./path/to/directory');
"packageManager"#>
- 类型:<string>
{
"packageManager": "<package manager name>@<version>"
}
packageManager 字段定义了在处理当前项目时预期使用的包管理器。它可以设置为任何 支持的包管理器,并将确保你的团队使用完全相同的包管理器版本,而无需安装除 Node.js 之外的任何其他软件。
【The "packageManager" field defines which package manager is expected to be
used when working on the current project. It can set to any of the
supported package managers, and will ensure that your teams use the exact
same package manager versions without having to install anything else than
Node.js.】
该字段目前处于实验阶段,需要选择加入;有关此操作的详细信息,请查看 Corepack 页面。
【This field is currently experimental and needs to be opted-in; check the Corepack page for details about the procedure.】
"type"#>
- 类型:<string>
"type" 字段定义了 Node.js 用于所有以该 package.json 文件作为最近父级的 .js 文件的模块格式。
【The "type" field defines the module format that Node.js uses for all
.js files that have that package.json file as their nearest parent.】
当最近的上级 package.json 文件包含顶层字段 "type" 且值为 "module" 时,以 .js 结尾的文件将作为 ES 模块加载。
【Files ending with .js are loaded as ES modules when the nearest parent
package.json file contains a top-level field "type" with a value of
"module".】
最近的父级 package.json 定义为在当前文件夹、该文件夹的父文件夹以及依次向上搜索时找到的第一个 package.json,直到遇到 node_modules 文件夹或卷根目录为止。
【The nearest parent package.json is defined as the first package.json found
when searching in the current folder, that folder's parent, and so on up
until a node_modules folder or the volume root is reached.】
// package.json
{
"type": "module"
}
# In same folder as preceding package.json
node my-app.js # Runs as ES module
如果最近的父级 package.json 缺少 "type" 字段,或者包含 "type": "commonjs",则 .js 文件被视为 CommonJS。如果到达卷根目录仍未找到 package.json,则 .js 文件被视为 CommonJS。
【If the nearest parent package.json lacks a "type" field, or contains
"type": "commonjs", .js files are treated as CommonJS. If the volume
root is reached and no package.json is found, .js files are treated as
CommonJS.】
如果最近的父级 package.json 包含 "type": "module",则 .js 文件的 import 语句会被视为 ES 模块。
// my-app.js, part of the same example as above
import './startup.js'; // Loaded as ES module because of package.json
无论 "type" 字段的值如何,.mjs 文件始终被视为 ES 模块,而 .cjs 文件始终被视为 CommonJS。
【Regardless of the value of the "type" field, .mjs files are always treated
as ES modules and .cjs files are always treated as CommonJS.】
"exports"#>
- 类型:<Object> | <string> | <string[]>
{
"exports": "./index.js"
}
“exports” 字段允许定义在通过名称导入包时的 入口点,无论是通过 node_modules 查找加载,还是通过 自我指涉 加载到其自身名称。它在 Node.js 12 及以上版本中受到支持,作为 "main" 的替代方案,可以支持定义 子路径导出 和 条件导出,同时封装内部未导出的模块。
【The "exports" field allows defining the entry points of a package when
imported by name loaded either via a node_modules lookup or a
self-reference to its own name. It is supported in Node.js 12+ as an
alternative to the "main" that can support defining subpath exports
and conditional exports while encapsulating internal unexported modules.】
条件性出口 也可以在 "exports" 中使用,以根据不同环境定义不同的包入口点,包括该包是通过 require 还是通过 import 引用。
exports 中定义的所有路径必须是以 ./ 开头的相对文件 URL。
【All paths defined in the "exports" must be relative file URLs starting with
./.】
"imports"#>
- 类型: <Object>
// package.json
{
"imports": {
"#dep": {
"node": "dep-node-native",
"default": "./dep-polyfill.js"
}
},
"dependencies": {
"dep-node-native": "^1.0.0"
}
}
imports 字段中的条目必须是以 # 开头的字符串。
【Entries in the imports field must be strings starting with #.】
包导入允许映射到外部包。
【Package imports permit mapping to external packages.】
此字段定义了当前包的 子路径导入。
【This field defines subpath imports for the current package.】