- 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 诊断报告
- sea 单个可执行应用程序
- stream 流
- stream/web 网络流
- string_decoder 字符串解码器
- test 测试
- timers 定时器
- tls 安全传输层
- trace_events 跟踪事件
- tty 终端
- url 网址
- util 实用工具
- v8 引擎
- vm 虚拟机
- wasi 网络汇编系统接口
- worker_threads 工作线程
- zlib 压缩
Node.js v20.18.0 文档
- Node.js v20.18.0
- 目录
-
导航
- 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 诊断报告
- sea 单个可执行应用程序
- stream 流
- stream/web 网络流
- string_decoder 字符串解码器
- test 测试
- timers 定时器
- tls 安全传输层
- trace_events 跟踪事件
- tty 终端
- url 网址
- util 实用工具
- v8 引擎
- vm 虚拟机
- wasi 网络汇编系统接口
- worker_threads 工作线程
- zlib 压缩
- 其他版本
模块:CommonJS 模块#
¥Modules: CommonJS modules
¥Stability: 2 - Stable
CommonJS 模块是为 Node.js 打包 JavaScript 代码的原始方式。Node.js 还支持浏览器和其他 JavaScript 运行时使用的 ECMAScript 模块 标准。
¥CommonJS modules are the original way to package JavaScript code for Node.js. Node.js also supports the ECMAScript modules standard used by browsers and other JavaScript runtimes.
在 Node.js 中,每个文件都被视为一个单独的模块。例如,假设一个名为 foo.js
的文件:
¥In Node.js, each file is treated as a separate module. For
example, consider a file named foo.js
:
const circle = require('./circle.js');
console.log(`The area of a circle of radius 4 is ${circle.area(4)}`);
在第一行,foo.js
加载了与 foo.js
位于同一目录中的模块 circle.js
。
¥On the first line, foo.js
loads the module circle.js
that is in the same
directory as foo.js
.
以下是 circle.js
的内容:
¥Here are the contents of circle.js
:
const { PI } = Math;
exports.area = (r) => PI * r ** 2;
exports.circumference = (r) => 2 * PI * r;
模块 circle.js
已导出函数 area()
和 circumference()
。通过在特殊的 exports
对象上指定额外的属性,将函数和对象添加到模块的根部。
¥The module circle.js
has exported the functions area()
and
circumference()
. Functions and objects are added to the root of a module
by specifying additional properties on the special exports
object.
模块的局部变量将是私有的,因为模块被 Node.js 封装在一个函数中(参见 模块封装器)。在此示例中,变量 PI
是 circle.js
私有的。
¥Variables local to the module will be private, because the module is wrapped
in a function by Node.js (see module wrapper).
In this example, the variable PI
is private to circle.js
.
可以为 module.exports
属性分配新的值(例如函数或对象)。
¥The module.exports
property can be assigned a new value (such as a function
or object).
在以下代码中,bar.js
使用 square
模块,该模块导出 Square 类:
¥In the following code, bar.js
makes use of the square
module, which exports
a Square class:
const Square = require('./square.js');
const mySquare = new Square(2);
console.log(`The area of mySquare is ${mySquare.area()}`);
square
模块在 square.js
中定义:
¥The square
module is defined in square.js
:
// Assigning to exports will not modify module, must use module.exports
module.exports = class Square {
constructor(width) {
this.width = width;
}
area() {
return this.width ** 2;
}
};
CommonJS 模块系统在 module
核心模块 中实现。
¥The CommonJS module system is implemented in the module
core module.
启用#
¥Enabling
Node.js 有两个模块系统:CommonJS 模块和 ECMAScript 模块。
¥Node.js has two module systems: CommonJS modules and ECMAScript modules.
默认情况下,Node.js 会将以下内容视为 CommonJS 模块:
¥By default, Node.js will treat the following as CommonJS modules:
-
扩展名为
.cjs
的文件;¥Files with a
.cjs
extension; -
当最近的父
package.json
文件包含值为"commonjs"
的顶层字段"type"
时,则扩展名为.js
的文件。¥Files with a
.js
extension when the nearest parentpackage.json
file contains a top-level field"type"
with a value of"commonjs"
. -
当最近的父
package.json
文件不包含顶层字段"type"
或任何父文件夹中都没有package.json
时,具有.js
扩展名或不带扩展名的文件;除非该文件包含错误的语法,除非它被评估为 ES 模块。包作者应该包括"type"
字段,即使在所有源都是 CommonJS 的包中也是如此。明确包的type
将使构建工具和加载器更容易确定包中的文件应该如何解释。¥Files with a
.js
extension or without an extension, when the nearest parentpackage.json
file doesn't contain a top-level field"type"
or there is nopackage.json
in any parent folder; unless the file contains syntax that errors unless it is evaluated as an ES module. Package authors should include the"type"
field, even in packages where all sources are CommonJS. Being explicit about thetype
of the package will make things easier for build tools and loaders to determine how the files in the package should be interpreted. -
扩展名不是
.mjs
、.cjs
、.json
、.node
或.js
的文件(当最近的父package.json
文件包含值为"module"
的顶层字段"type"
时,这些文件将被识别为 CommonJS 模块只有当它们是 通过require()
包含,而不是用作程序的命令行入口点时)。¥Files with an extension that is not
.mjs
,.cjs
,.json
,.node
, or.js
(when the nearest parentpackage.json
file contains a top-level field"type"
with a value of"module"
, those files will be recognized as CommonJS modules only if they are being included viarequire()
, not when used as the command-line entry point of the program).
有关详细信息,请参阅 确定模块系统。
¥See Determining module system for more details.
调用 require()
始终使用 CommonJS 模块加载器。调用 import()
始终使用 ECMAScript 模块加载器。
¥Calling require()
always use the CommonJS module loader. Calling import()
always use the ECMAScript module loader.
访问主模块#
¥Accessing the main module
当文件直接从 Node.js 运行时,则 require.main
被设置为其 module
。这意味着可以通过测试 require.main === module
来确定文件是否被直接运行。
¥When a file is run directly from Node.js, require.main
is set to its
module
. That means that it is possible to determine whether a file has been
run directly by testing require.main === module
.
对于文件 foo.js
,如果通过 node foo.js
运行,则为 true
,如果通过 require('./foo')
运行,则为 false
。
¥For a file foo.js
, this will be true
if run via node foo.js
, but
false
if run by require('./foo')
.
当入口点不是 CommonJS 模块时,则 require.main
为 undefined
,且主模块不可达。
¥When the entry point is not a CommonJS module, require.main
is undefined
,
and the main module is out of reach.
包管理器提示#
¥Package manager tips
Node.js require()
函数的语义被设计为足够通用以支持合理的目录结构。诸如 dpkg
、rpm
和 npm
之类的包管理器程序有望发现无需修改即可从 Node.js 模块构建原生包。
¥The semantics of the Node.js require()
function were designed to be general
enough to support reasonable directory structures. Package manager programs
such as dpkg
, rpm
, and npm
will hopefully find it possible to build
native packages from Node.js modules without modification.
下面,我们给出了一个可行的建议目录结构:
¥In the following, we give a suggested directory structure that could work:
假设想让位于 /usr/lib/node/<some-package>/<some-version>
的文件夹保存特定版本包的内容。
¥Let's say that we wanted to have the folder at
/usr/lib/node/<some-package>/<some-version>
hold the contents of a
specific version of a package.
包可以相互依赖。为了安装包 foo
,可能需要安装包 bar
的特定版本。bar
包本身可能存在依赖,在某些情况下,这些甚至可能发生冲突或形成循环依赖。
¥Packages can depend on one another. In order to install package foo
, it
may be necessary to install a specific version of package bar
. The bar
package may itself have dependencies, and in some cases, these may even collide
or form cyclic dependencies.
因为 Node.js 查找它加载的任何模块的 realpath
(即它解析符号链接)然后是 在 node_modules
文件夹中查找它们的依赖,这种情况可以通过以下架构解决:
¥Because Node.js looks up the realpath
of any modules it loads (that is, it
resolves symlinks) and then looks for their dependencies in node_modules
folders,
this situation can be resolved with the following architecture:
-
/usr/lib/node/foo/1.2.3/
:foo
包的内容,版本 1.2.3。¥
/usr/lib/node/foo/1.2.3/
: Contents of thefoo
package, version 1.2.3. -
/usr/lib/node/bar/4.3.2/
:foo
依赖的bar
包的内容。¥
/usr/lib/node/bar/4.3.2/
: Contents of thebar
package thatfoo
depends on. -
/usr/lib/node/foo/1.2.3/node_modules/bar
:/usr/lib/node/bar/4.3.2/
的符号链接。¥
/usr/lib/node/foo/1.2.3/node_modules/bar
: Symbolic link to/usr/lib/node/bar/4.3.2/
. -
/usr/lib/node/bar/4.3.2/node_modules/*
:bar
依赖的包的符号链接。¥
/usr/lib/node/bar/4.3.2/node_modules/*
: Symbolic links to the packages thatbar
depends on.
因此,即使遇到循环,或者如果存在依赖冲突,每个模块都将能够获得它可以使用的依赖版本。
¥Thus, even if a cycle is encountered, or if there are dependency conflicts, every module will be able to get a version of its dependency that it can use.
当 foo
包中的代码执行 require('bar')
时,它将获得符号链接到 /usr/lib/node/foo/1.2.3/node_modules/bar
的版本。然后,当 bar
包中的代码调用 require('quux')
时,它将获得符号链接到 /usr/lib/node/bar/4.3.2/node_modules/quux
的版本。
¥When the code in the foo
package does require('bar')
, it will get the
version that is symlinked into /usr/lib/node/foo/1.2.3/node_modules/bar
.
Then, when the code in the bar
package calls require('quux')
, it'll get
the version that is symlinked into
/usr/lib/node/bar/4.3.2/node_modules/quux
.
此外,为了使模块查找过程更加优化,与其将包直接放在 /usr/lib/node
中,还可以将它们放在 /usr/lib/node_modules/<name>/<version>
中。这样 Node.js 就不会费心寻找 /usr/node_modules
或 /node_modules
中缺失的依赖了。
¥Furthermore, to make the module lookup process even more optimal, rather
than putting packages directly in /usr/lib/node
, we could put them in
/usr/lib/node_modules/<name>/<version>
. Then Node.js will not bother
looking for missing dependencies in /usr/node_modules
or /node_modules
.
为了使模块可用于 Node.js 交互式解释器,将 /usr/lib/node_modules
文件夹添加到 $NODE_PATH
环境变量可能会很有用。由于使用 node_modules
文件夹的模块查找都是相对的,并且基于调用 require()
的文件的真实路径,因此包本身可以位于任何位置。
¥In order to make modules available to the Node.js REPL, it might be useful to
also add the /usr/lib/node_modules
folder to the $NODE_PATH
environment
variable. Since the module lookups using node_modules
folders are all
relative, and based on the real path of the files making the calls to
require()
, the packages themselves can be anywhere.
使用 require()
加载 ECMAScript 模块#
¥Loading ECMAScript modules using require()
.mjs
分机保留给 ECMAScript 模块。目前,如果不使用标志 --experimental-require-module
,使用 require()
加载 ECMAScript 模块将抛出 ERR_REQUIRE_ESM
错误,用户需要使用 import()
代替。有关哪些文件被解析为 ECMAScript 模块的更多信息,请参阅 确定模块系统 部分。
¥The .mjs
extension is reserved for ECMAScript Modules.
Currently, if the flag --experimental-require-module
is not used, loading
an ECMAScript module using require()
will throw a ERR_REQUIRE_ESM
error, and users need to use import()
instead. See
Determining module system section for more info
regarding which files are parsed as ECMAScript modules.
如果启用了 --experimental-require-module
,并且 require()
加载的 ECMAScript 模块满足以下要求:
¥If --experimental-require-module
is enabled, and the ECMAScript module being
loaded by require()
meets the following requirements:
-
在最近的 package.json 或
.mjs
扩展中用"type": "module"
字段明确标记为 ES 模块。¥Explicitly marked as an ES module with a
"type": "module"
field in the closest package.json or a.mjs
extension. -
完全同步(不包含顶层
await
)。¥Fully synchronous (contains no top-level
await
).
require()
将请求的模块作为 ES 模块加载,并返回模块名称空间对象。在这种情况下,它与动态 import()
类似,但同步运行并直接返回名称空间对象。
¥require()
will load the requested module as an ES Module, and return
the module name space object. In this case it is similar to dynamic
import()
but is run synchronously and returns the name space object
directly.
// point.mjs
export function distance(a, b) { return (b.x - a.x) ** 2 + (b.y - a.y) ** 2; }
class Point {
constructor(x, y) { this.x = x; this.y = y; }
}
export default Point;
const required = require('./point.mjs');
// [Module: null prototype] {
// default: [class Point],
// distance: [Function: distance]
// }
console.log(required);
(async () => {
const imported = await import('./point.mjs');
console.log(imported === required); // true
})();
如果 require()
的模块包含顶层 await
,或者 import
的模块图包含顶层 await
,则将抛出 ERR_REQUIRE_ASYNC_MODULE
。在这种情况下,用户应该使用 import()
加载异步模块。
¥If the module being require()
'd contains top-level await
, or the module
graph it import
s contains top-level await
,
ERR_REQUIRE_ASYNC_MODULE
will be thrown. In this case, users should
load the asynchronous module using import()
.
如果启用了 --experimental-print-required-tla
,Node.js 将评估模块,尝试找到顶层等待,并打印其位置以帮助用户修复它们,而不是在评估之前抛出 ERR_REQUIRE_ASYNC_MODULE
。
¥If --experimental-print-required-tla
is enabled, instead of throwing
ERR_REQUIRE_ASYNC_MODULE
before evaluation, Node.js will evaluate the
module, try to locate the top-level awaits, and print their location to
help users fix them.
全部一起#
¥All together
要获取调用 require()
时将加载的确切文件名,则使用 require.resolve()
函数。
¥To get the exact filename that will be loaded when require()
is called, use
the require.resolve()
function.
综上所述,这里是 require()
的伪代码高级算法:
¥Putting together all of the above, here is the high-level algorithm
in pseudocode of what require()
does:
require(X) from module at path Y
1. If X is a core module,
a. return the core module
b. STOP
2. If X begins with '/'
a. set Y to be the file system root
3. If X begins with './' or '/' or '../'
a. LOAD_AS_FILE(Y + X)
b. LOAD_AS_DIRECTORY(Y + X)
c. THROW "not found"
4. If X begins with '#'
a. LOAD_PACKAGE_IMPORTS(X, dirname(Y))
5. LOAD_PACKAGE_SELF(X, dirname(Y))
6. LOAD_NODE_MODULES(X, dirname(Y))
7. THROW "not found"
LOAD_AS_FILE(X)
1. If X is a file, load X as its file extension format. STOP
2. If X.js is a file,
a. Find the closest package scope SCOPE to X.
b. If no scope was found, load X.js as a CommonJS module. STOP.
c. If the SCOPE/package.json contains "type" field,
1. If the "type" field is "module", load X.js as an ECMAScript module. STOP.
2. Else, load X.js as an CommonJS module. STOP.
3. If X.json is a file, load X.json to a JavaScript Object. STOP
4. If X.node is a file, load X.node as binary addon. STOP
5. If X.mjs is a file, and `--experimental-require-module` is enabled,
load X.mjs as an ECMAScript module. STOP
LOAD_INDEX(X)
1. If X/index.js is a file
a. Find the closest package scope SCOPE to X.
b. If no scope was found, load X/index.js as a CommonJS module. STOP.
c. If the SCOPE/package.json contains "type" field,
1. If the "type" field is "module", load X/index.js as an ECMAScript module. STOP.
2. Else, load X/index.js as an CommonJS module. STOP.
2. If X/index.json is a file, parse X/index.json to a JavaScript object. STOP
3. If X/index.node is a file, load X/index.node as binary addon. STOP
LOAD_AS_DIRECTORY(X)
1. If X/package.json is a file,
a. Parse X/package.json, and look for "main" field.
b. If "main" is a falsy value, GOTO 2.
c. let M = X + (json main field)
d. LOAD_AS_FILE(M)
e. LOAD_INDEX(M)
f. LOAD_INDEX(X) DEPRECATED
g. THROW "not found"
2. LOAD_INDEX(X)
LOAD_NODE_MODULES(X, START)
1. let DIRS = NODE_MODULES_PATHS(START)
2. for each DIR in DIRS:
a. LOAD_PACKAGE_EXPORTS(X, DIR)
b. LOAD_AS_FILE(DIR/X)
c. LOAD_AS_DIRECTORY(DIR/X)
NODE_MODULES_PATHS(START)
1. let PARTS = path split(START)
2. let I = count of PARTS - 1
3. let DIRS = []
4. while I >= 0,
a. if PARTS[I] = "node_modules" CONTINUE
b. DIR = path join(PARTS[0 .. I] + "node_modules")
c. DIRS = DIR + DIRS
d. let I = I - 1
5. return DIRS + GLOBAL_FOLDERS
LOAD_PACKAGE_IMPORTS(X, DIR)
1. Find the closest package scope SCOPE to DIR.
2. If no scope was found, return.
3. If the SCOPE/package.json "imports" is null or undefined, return.
4. let MATCH = PACKAGE_IMPORTS_RESOLVE(X, pathToFileURL(SCOPE),
["node", "require"]) <a href="esm.md#resolver-algorithm-specification">defined in the ESM resolver</a>.
5. RESOLVE_ESM_MATCH(MATCH).
LOAD_PACKAGE_EXPORTS(X, DIR)
1. Try to interpret X as a combination of NAME and SUBPATH where the name
may have a @scope/ prefix and the subpath begins with a slash (`/`).
2. If X does not match this pattern or DIR/NAME/package.json is not a file,
return.
3. Parse DIR/NAME/package.json, and look for "exports" field.
4. If "exports" is null or undefined, return.
5. let MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(DIR/NAME), "." + SUBPATH,
`package.json` "exports", ["node", "require"]) <a href="esm.md#resolver-algorithm-specification">defined in the ESM resolver</a>.
6. RESOLVE_ESM_MATCH(MATCH)
LOAD_PACKAGE_SELF(X, DIR)
1. Find the closest package scope SCOPE to DIR.
2. If no scope was found, return.
3. If the SCOPE/package.json "exports" is null or undefined, return.
4. If the SCOPE/package.json "name" is not the first segment of X, return.
5. let MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(SCOPE),
"." + X.slice("name".length), `package.json` "exports", ["node", "require"])
<a href="esm.md#resolver-algorithm-specification">defined in the ESM resolver</a>.
6. RESOLVE_ESM_MATCH(MATCH)
RESOLVE_ESM_MATCH(MATCH)
1. let RESOLVED_PATH = fileURLToPath(MATCH)
2. If the file at RESOLVED_PATH exists, load RESOLVED_PATH as its extension
format. STOP
3. THROW "not found"
缓存#
¥Caching
模块在第一次加载后被缓存。这意味着(类似其他缓存)每次调用 require('foo')
都会返回完全相同的对象(如果解析为相同的文件)。
¥Modules are cached after the first time they are loaded. This means (among other
things) that every call to require('foo')
will get exactly the same object
returned, if it would resolve to the same file.
如果 require.cache
没有被修改,则多次调用 require('foo')
不会导致模块代码被多次执行。这是重要的特性。有了它,可以返回 "部分完成" 对象,从而允许加载传递依赖,即使它们会导致循环。
¥Provided require.cache
is not modified, multiple calls to require('foo')
will not cause the module code to be executed multiple times. This is an
important feature. With it, "partially done" objects can be returned, thus
allowing transitive dependencies to be loaded even when they would cause cycles.
要让模块多次执行代码,则导出函数,然后调用该函数。
¥To have a module execute code multiple times, export a function, and call that function.
模块缓存注意事项#
¥Module caching caveats
模块根据其解析的文件名进行缓存。由于模块可能会根据调用模块的位置(从 node_modules
文件夹加载)解析为不同的文件名,因此如果 require('foo')
解析为不同的文件,则不能保证它始终返回完全相同的对象。
¥Modules are cached based on their resolved filename. Since modules may resolve
to a different filename based on the location of the calling module (loading
from node_modules
folders), it is not a guarantee that require('foo')
will
always return the exact same object, if it would resolve to different files.
此外,在不区分大小写的文件系统或操作系统上,不同的解析文件名可以指向同一个文件,但缓存仍会将它们视为不同的模块,并将多次重新加载文件。例如,require('./foo')
和 require('./FOO')
返回两个不同的对象,而不管 ./foo
和 ./FOO
是否是同一个文件。
¥Additionally, on case-insensitive file systems or operating systems, different
resolved filenames can point to the same file, but the cache will still treat
them as different modules and will reload the file multiple times. For example,
require('./foo')
and require('./FOO')
return two different objects,
irrespective of whether or not ./foo
and ./FOO
are the same file.
内置模块#
¥Built-in modules
Node.js 有些模块编译成二进制文件。这些模块在本文档的其他地方有更详细的描述。
¥Node.js has several modules compiled into the binary. These modules are described in greater detail elsewhere in this documentation.
内置模块在 Node.js 源中定义,位于 lib/
文件夹中。
¥The built-in modules are defined within the Node.js source and are located in the
lib/
folder.
可以使用 node:
前缀标识内置模块,在这种情况下它会绕过 require
缓存。例如,require('node:http')
将始终返回内置的 HTTP 模块,即使有该名称的 require.cache
条目。
¥Built-in modules can be identified using the node:
prefix, in which case
it bypasses the require
cache. For instance, require('node:http')
will
always return the built in HTTP module, even if there is require.cache
entry
by that name.
如果某些内置模块的标识符传递给 require()
,则始终优先加载它们。例如,require('http')
将始终返回内置的 HTTP 模块,即使存在该名称的文件。无需使用 node:
前缀即可加载的内置模块列表显示为 module.builtinModules
。
¥Some built-in modules are always preferentially loaded if their identifier is
passed to require()
. For instance, require('http')
will always
return the built-in HTTP module, even if there is a file by that name. The list
of built-in modules that can be loaded without using the node:
prefix is exposed
as module.builtinModules
.
使用强制 node:
前缀的内置模块#
¥Built-in modules with mandatory node:
prefix
当由 require()
加载时,某些内置模块必须使用 node:
前缀进行请求。此要求是为了防止新引入的内置模块与已经采用该名称的用户空间包发生冲突。目前需要 node:
前缀的内置模块是:
¥When being loaded by require()
, some built-in modules must be requested with the
node:
prefix. This requirement exists to prevent newly introduced built-in
modules from having a conflict with user land packages that already have
taken the name. Currently the built-in modules that requires the node:
prefix are:
循环#
¥Cycles
当有循环 require()
调用时,模块在返回时可能尚未完成执行。
¥When there are circular require()
calls, a module might not have finished
executing when it is returned.
考虑这种情况:
¥Consider this situation:
a.js
:
console.log('a starting');
exports.done = false;
const b = require('./b.js');
console.log('in a, b.done = %j', b.done);
exports.done = true;
console.log('a done');
b.js
:
console.log('b starting');
exports.done = false;
const a = require('./a.js');
console.log('in b, a.done = %j', a.done);
exports.done = true;
console.log('b done');
main.js
:
console.log('main starting');
const a = require('./a.js');
const b = require('./b.js');
console.log('in main, a.done = %j, b.done = %j', a.done, b.done);
当 main.js
加载 a.js
时,a.js
依次加载 b.js
。此时,b.js
尝试加载 a.js
。为了防止无限循环,a.js
导出对象的未完成副本将返回到 b.js
模块。然后 b.js
完成加载,并将其 exports
对象提供给 a.js
模块。
¥When main.js
loads a.js
, then a.js
in turn loads b.js
. At that
point, b.js
tries to load a.js
. In order to prevent an infinite
loop, an unfinished copy of the a.js
exports object is returned to the
b.js
module. b.js
then finishes loading, and its exports
object is
provided to the a.js
module.
到 main.js
加载这两个模块时,它们都已完成。因此,该程序的输出将是:
¥By the time main.js
has loaded both modules, they're both finished.
The output of this program would thus be:
$ node main.js
main starting
a starting
b starting
in b, a.done = false
b done
in a, b.done = true
a done
in main, a.done = true, b.done = true
需要仔细规划以允许循环模块依赖在应用中正常工作。
¥Careful planning is required to allow cyclic module dependencies to work correctly within an application.
文件模块#
¥File modules
如果找不到确切的文件名,那么 Node.js 将尝试加载具有添加的扩展名的所需文件名:.js
、.json
,最后是 .node
。当加载具有不同扩展名的文件(例如 .cjs
)时,则必须将其全名传给 require()
,包括其文件扩展名(例如 require('./file.cjs')
)。
¥If the exact filename is not found, then Node.js will attempt to load the
required filename with the added extensions: .js
, .json
, and finally
.node
. When loading a file that has a different extension (e.g. .cjs
), its
full name must be passed to require()
, including its file extension (e.g.
require('./file.cjs')
).
.json
文件被解析为 JSON 文本文件,.node
文件被解释为加载了 process.dlopen()
的已编译插件模块。使用任何其他扩展名(或根本没有扩展名)的文件被解析为 JavaScript 文本文件。请参阅 确定模块系统 部分以了解将使用什么解析目标。
¥.json
files are parsed as JSON text files, .node
files are interpreted as
compiled addon modules loaded with process.dlopen()
. Files using any other
extension (or no extension at all) are parsed as JavaScript text files. Refer to
the Determining module system section to understand what parse goal will be
used.
以 '/'
为前缀的必需模块是文件的绝对路径。例如,require('/home/marco/foo.js')
将在 /home/marco/foo.js
加载文件。
¥A required module prefixed with '/'
is an absolute path to the file. For
example, require('/home/marco/foo.js')
will load the file at
/home/marco/foo.js
.
以 './'
为前缀的必需模块与调用 require()
的文件相关。也就是说,circle.js
必须和 foo.js
在同一个目录下,require('./circle')
才能找到它。
¥A required module prefixed with './'
is relative to the file calling
require()
. That is, circle.js
must be in the same directory as foo.js
for
require('./circle')
to find it.
如果没有前导 '/'
、'./'
或 '../'
来指示文件,则该模块必须是核心模块或从 node_modules
文件夹加载。
¥Without a leading '/'
, './'
, or '../'
to indicate a file, the module must
either be a core module or is loaded from a node_modules
folder.
如果给定路径不存在,则 require()
将抛出 MODULE_NOT_FOUND
错误。
¥If the given path does not exist, require()
will throw a
MODULE_NOT_FOUND
error.
文件夹作为模块#
¥Folders as modules
¥Stability: 3 - Legacy: Use subpath exports or subpath imports instead.
可以通过三种方式将文件夹作为参数传给 require()
。
¥There are three ways in which a folder may be passed to require()
as
an argument.
首先是在文件夹的根目录创建 package.json
文件,指定 main
模块。一个示例 package.json
文件可能如下所示:
¥The first is to create a package.json
file in the root of the folder,
which specifies a main
module. An example package.json
file might
look like this:
{ "name" : "some-library",
"main" : "./lib/some-library.js" }
如果这是在 ./some-library
的文件夹中,则 require('./some-library')
将尝试加载 ./some-library/lib/some-library.js
。
¥If this was in a folder at ./some-library
, then
require('./some-library')
would attempt to load
./some-library/lib/some-library.js
.
如果目录中不存在 package.json
文件,或者 "main"
条目丢失或无法解析,则 Node.js 将尝试从该目录中加载 index.js
或 index.node
文件。例如,如果前面的示例中没有 package.json
文件,则 require('./some-library')
将尝试加载:
¥If there is no package.json
file present in the directory, or if the
"main"
entry is missing or cannot be resolved, then Node.js
will attempt to load an index.js
or index.node
file out of that
directory. For example, if there was no package.json
file in the previous
example, then require('./some-library')
would attempt to load:
-
./some-library/index.js
-
./some-library/index.node
如果这些尝试失败,Node.js 将报告整个模块丢失,并显示默认错误:
¥If these attempts fail, then Node.js will report the entire module as missing with the default error:
Error: Cannot find module 'some-library'
在上述所有三种情况下,import('./some-library')
调用都将导致 ERR_UNSUPPORTED_DIR_IMPORT
错误。使用包 子路径导出 或 子路径导入 可以提供与文件夹作为模块相同的包含组织优势,并且适用于 require
和 import
。
¥In all three above cases, an import('./some-library')
call would result in a
ERR_UNSUPPORTED_DIR_IMPORT
error. Using package subpath exports or
subpath imports can provide the same containment organization benefits as
folders as modules, and work for both require
and import
.
从 node_modules
文件夹加载#
¥Loading from node_modules
folders
如果传递给 require()
的模块标识符不是 built-in 模块,并且不是以 '/'
、'../'
或 './'
开头,则 Node.js 从当前模块的目录开始,并添加 /node_modules
,并尝试从中加载模块 地点。Node.js 不会将 node_modules
附加到已经以 node_modules
结尾的路径。
¥If the module identifier passed to require()
is not a
built-in module, and does not begin with '/'
, '../'
, or
'./'
, then Node.js starts at the directory of the current module, and
adds /node_modules
, and attempts to load the module from that location.
Node.js will not append node_modules
to a path already ending in
node_modules
.
如果在那里找不到它,则它移动到父目录,依此类推,直到到达文件系统的根目录。
¥If it is not found there, then it moves to the parent directory, and so on, until the root of the file system is reached.
例如,如果 '/home/ry/projects/foo.js'
处的文件调用 require('bar.js')
,则 Node.js 将按以下顺序查找以下位置:
¥For example, if the file at '/home/ry/projects/foo.js'
called
require('bar.js')
, then Node.js would look in the following locations, in
this order:
-
/home/ry/projects/node_modules/bar.js
-
/home/ry/node_modules/bar.js
-
/home/node_modules/bar.js
-
/node_modules/bar.js
这允许程序本地化它们的依赖,这样它们就不会发生冲突。
¥This allows programs to localize their dependencies, so that they do not clash.
通过在模块名称后包含路径后缀,可以要求与模块一起分发的特定文件或子模块。例如,require('example-module/path/to/file')
将相对于 example-module
所在的位置解析 path/to/file
。后缀路径遵循相同的模块解析语义。
¥It is possible to require specific files or sub modules distributed with a
module by including a path suffix after the module name. For instance
require('example-module/path/to/file')
would resolve path/to/file
relative to where example-module
is located. The suffixed path follows the
same module resolution semantics.
从全局文件夹加载#
¥Loading from the global folders
如果 NODE_PATH
环境变量设置为以冒号分隔的绝对路径列表,则 Node.js 将在这些路径中搜索模块(如果它们在其他地方找不到)。
¥If the NODE_PATH
environment variable is set to a colon-delimited list
of absolute paths, then Node.js will search those paths for modules if they
are not found elsewhere.
在 Windows 上,NODE_PATH
由分号 (;
) 而不是冒号分隔。
¥On Windows, NODE_PATH
is delimited by semicolons (;
) instead of colons.
在定义当前的 模块解析 算法之前,NODE_PATH
最初是为了支持从不同路径加载模块而创建的。
¥NODE_PATH
was originally created to support loading modules from
varying paths before the current module resolution algorithm was defined.
NODE_PATH
仍然受支持,但现在 Node.js 生态系统已经确定了用于定位依赖模块的约定,因此不太必要。有时,当不知道必须设置 NODE_PATH
时,依赖 NODE_PATH
的部署会表现出意外的行为。有时,模块的依赖会发生变化,导致在搜索 NODE_PATH
时加载不同的版本(甚至不同的模块)。
¥NODE_PATH
is still supported, but is less necessary now that the Node.js
ecosystem has settled on a convention for locating dependent modules.
Sometimes deployments that rely on NODE_PATH
show surprising behavior
when people are unaware that NODE_PATH
must be set. Sometimes a
module's dependencies change, causing a different version (or even a
different module) to be loaded as the NODE_PATH
is searched.
此外,Node.js 将在以下 GLOBAL_FOLDERS 列表中搜索:
¥Additionally, Node.js will search in the following list of GLOBAL_FOLDERS:
-
1:
$HOME/.node_modules
-
2:
$HOME/.node_libraries
-
3:
$PREFIX/lib/node
其中 $HOME
是用户的主目录,$PREFIX
是 Node.js 配置的 node_prefix
。
¥Where $HOME
is the user's home directory, and $PREFIX
is the Node.js
configured node_prefix
.
这些主要是出于历史原因。
¥These are mostly for historic reasons.
强烈建议将依赖放在本地 node_modules
文件夹中。这些将加载得更快,更可靠。
¥It is strongly encouraged to place dependencies in the local node_modules
folder. These will be loaded faster, and more reliably.
模块封装器#
¥The module wrapper
在执行模块代码之前,Node.js 将使用如下所示的函数封装器对其进行封装:
¥Before a module's code is executed, Node.js will wrap it with a function wrapper that looks like the following:
(function(exports, require, module, __filename, __dirname) {
// Module code actually lives in here
});
通过这样做,Node.js 实现了以下几点:
¥By doing this, Node.js achieves a few things:
-
它将顶层变量(使用
var
、const
或let
定义)保持在模块而不是全局对象的范围内。¥It keeps top-level variables (defined with
var
,const
, orlet
) scoped to the module rather than the global object. -
它有助于提供一些实际特定于模块的全局变量,例如:
¥It helps to provide some global-looking variables that are actually specific to the module, such as:
-
module
和exports
对象,实现者可以用来从模块中导出值。¥The
module
andexports
objects that the implementor can use to export values from the module. -
便利变量
__filename
和__dirname
,包含模块的绝对文件名和目录路径。¥The convenience variables
__filename
and__dirname
, containing the module's absolute filename and directory path.
-
模块作用域#
¥The module scope
__dirname
#
当前模块的目录名。这与 __filename
的 path.dirname()
相同。
¥The directory name of the current module. This is the same as the
path.dirname()
of the __filename
.
示例:从 /Users/mjr
运行 node example.js
¥Example: running node example.js
from /Users/mjr
console.log(__dirname);
// Prints: /Users/mjr
console.log(path.dirname(__filename));
// Prints: /Users/mjr
__filename
#
当前模块的文件名。这是当前模块文件的已解析符号链接的绝对路径。
¥The file name of the current module. This is the current module file's absolute path with symlinks resolved.
对于主程序,这不一定与命令行中使用的文件名相同。
¥For a main program this is not necessarily the same as the file name used in the command line.
当前模块的目录名见 __dirname
。
¥See __dirname
for the directory name of the current module.
示例:
¥Examples:
从 /Users/mjr
运行 node example.js
¥Running node example.js
from /Users/mjr
console.log(__filename);
// Prints: /Users/mjr/example.js
console.log(__dirname);
// Prints: /Users/mjr
给定两个模块:a
和 b
,其中 b
是 a
的依赖,目录结构为:
¥Given two modules: a
and b
, where b
is a dependency of
a
and there is a directory structure of:
-
/Users/mjr/app/a.js
-
/Users/mjr/app/node_modules/b/b.js
在 b.js
中对 __filename
的引用将返回 /Users/mjr/app/node_modules/b/b.js
,而在 a.js
中对 __filename
的引用将返回 /Users/mjr/app/a.js
。
¥References to __filename
within b.js
will return
/Users/mjr/app/node_modules/b/b.js
while references to __filename
within
a.js
will return /Users/mjr/app/a.js
.
exports
#
对 module.exports
的引用,其输入更短。有关何时使用 exports
和何时使用 module.exports
的详细信息,请参阅有关 导出快捷方式 的部分。
¥A reference to the module.exports
that is shorter to type.
See the section about the exports shortcut for details on when to use
exports
and when to use module.exports
.
module
#
对当前模块的引用,请参阅有关 module
对象 的部分。特别是,module.exports
用于定义模块通过 require()
导出和提供的内容。
¥A reference to the current module, see the section about the
module
object. In particular, module.exports
is used for defining what
a module exports and makes available through require()
.
require(id)
#
-
id
<string> 模块名称或路径¥
id
<string> module name or path -
返回:<any> 导出的模块内容
¥Returns: <any> exported module content
用于导入模块、JSON
和本地文件。模块可以从 node_modules
导入。可以使用相对路径(例如 ./
、./foo
、./bar/baz
、../foo
)导入本地模块和 JSON 文件,该路径将根据 __dirname
(如果有定义)命名的目录或当前工作目录进行解析。POSIX 风格的相对路径以独立于操作系统的方式解析,这意味着上面的示例将在 Windows 上以与在 Unix 系统上相同的方式工作。
¥Used to import modules, JSON
, and local files. Modules can be imported
from node_modules
. Local modules and JSON files can be imported using
a relative path (e.g. ./
, ./foo
, ./bar/baz
, ../foo
) that will be
resolved against the directory named by __dirname
(if defined) or
the current working directory. The relative paths of POSIX style are resolved
in an OS independent fashion, meaning that the examples above will work on
Windows in the same way they would on Unix systems.
// Importing a local module with a path relative to the `__dirname` or current
// working directory. (On Windows, this would resolve to .\path\myLocalModule.)
const myLocalModule = require('./path/myLocalModule');
// Importing a JSON file:
const jsonData = require('./path/filename.json');
// Importing a module from node_modules or Node.js built-in module:
const crypto = require('node:crypto');
require.cache
#
模块在需要时缓存在此对象中。通过从此对象中删除键值,下一次 require
将重新加载模块。这不适用于 原生插件,重新加载将导致错误。
¥Modules are cached in this object when they are required. By deleting a key
value from this object, the next require
will reload the module.
This does not apply to native addons, for which reloading will result in an
error.
添加或替换条目也是可能的。在内置模块之前检查此缓存,如果将与内置模块匹配的名称添加到缓存中,则只有 node:
前缀的 require 调用将接收内置模块。小心使用!
¥Adding or replacing entries is also possible. This cache is checked before
built-in modules and if a name matching a built-in module is added to the cache,
only node:
-prefixed require calls are going to receive the built-in module.
Use with care!
const assert = require('node:assert');
const realFs = require('node:fs');
const fakeFs = {};
require.cache.fs = { exports: fakeFs };
assert.strictEqual(require('fs'), fakeFs);
assert.strictEqual(require('node:fs'), realFs);
require.extensions
#
¥Stability: 0 - Deprecated
指导 require
如何处理某些文件扩展名。
¥Instruct require
on how to handle certain file extensions.
将扩展名为 .sjs
的文件处理为 .js
:
¥Process files with the extension .sjs
as .js
:
require.extensions['.sjs'] = require.extensions['.js'];
已弃用。过去,此列表已用于通过按需编译将非 JavaScript 模块加载到 Node.js 中。但是,在实践中,有很多更好的方法可以做到这一点,例如通过其他一些 Node.js 程序加载模块,或者提前将它们编译为 JavaScript。
¥Deprecated. In the past, this list has been used to load non-JavaScript modules into Node.js by compiling them on-demand. However, in practice, there are much better ways to do this, such as loading modules via some other Node.js program, or compiling them to JavaScript ahead of time.
避免使用 require.extensions
。使用可能会导致细微的错误,并且每个注册的扩展程序解决扩展程序的速度都会变慢。
¥Avoid using require.extensions
. Use could cause subtle bugs and resolving the
extensions gets slower with each registered extension.
require.main
#
Module
对象代表 Node.js 进程启动时加载的入口脚本,如果程序的入口点不是 CommonJS 模块,则为 undefined
。参见 "访问主模块"。
¥The Module
object representing the entry script loaded when the Node.js
process launched, or undefined
if the entry point of the program is not a
CommonJS module.
See "Accessing the main module".
在 entry.js
脚本中:
¥In entry.js
script:
console.log(require.main);
node entry.js
Module {
id: '.',
path: '/absolute/path/to',
exports: {},
filename: '/absolute/path/to/entry.js',
loaded: false,
children: [],
paths:
[ '/absolute/path/to/node_modules',
'/absolute/path/node_modules',
'/absolute/node_modules',
'/node_modules' ] }
require.resolve(request[, options])
#
-
request
<string> 要解析的模块路径。¥
request
<string> The module path to resolve. -
options
<Object>-
paths
<string[]> 从中解析模块位置的路径。如果存在,将使用这些路径而不是默认解析路径,但 GLOBAL_FOLDERS 和$HOME/.node_modules
除外,它们始终包含在内。这些路径中的每一个都用作模块解析算法的起点,这意味着从此位置检查node_modules
层级。¥
paths
<string[]> Paths to resolve module location from. If present, these paths are used instead of the default resolution paths, with the exception of GLOBAL_FOLDERS like$HOME/.node_modules
, which are always included. Each of these paths is used as a starting point for the module resolution algorithm, meaning that thenode_modules
hierarchy is checked from this location.
-
-
返回:<string>
¥Returns: <string>
使用内部的 require()
工具查找模块的位置,但不加载模块,只返回解析的文件名。
¥Use the internal require()
machinery to look up the location of a module,
but rather than loading the module, just return the resolved filename.
如果找不到模块,则会抛出 MODULE_NOT_FOUND
错误。
¥If the module can not be found, a MODULE_NOT_FOUND
error is thrown.
require.resolve.paths(request)
#
-
request
<string> 正在检索其查找路径的模块路径。¥
request
<string> The module path whose lookup paths are being retrieved. -
返回:<string[]> | <null>
¥Returns: <string[]> | <null>
如果 request
字符串引用核心模块,例如 http
或 fs
,则返回包含在解析 request
或 null
期间搜索的路径的数组。
¥Returns an array containing the paths searched during resolution of request
or
null
if the request
string references a core module, for example http
or
fs
.
module
对象#
¥The module
object
在每个模块中,module
自由变量是对代表当前模块的对象的引用。为方便起见,module.exports
也可通过 exports
模块全局访问。module
实际上不是全局的,而是每个模块本地的。
¥In each module, the module
free variable is a reference to the object
representing the current module. For convenience, module.exports
is
also accessible via the exports
module-global. module
is not actually
a global but rather local to each module.
module.children
#
这个模块首次需要的对象。
¥The module objects required for the first time by this one.
module.exports
#
module.exports
对象由 Module
系统创建。有时这是不可接受的;许多人希望他们的模块是某个类的实例。为此,则将所需的导出对象赋值给 module.exports
。将所需的对象赋值给 exports
只会重新绑定本地的 exports
变量,这可能不是想要的。
¥The module.exports
object is created by the Module
system. Sometimes this is
not acceptable; many want their module to be an instance of some class. To do
this, assign the desired export object to module.exports
. Assigning
the desired object to exports
will simply rebind the local exports
variable,
which is probably not what is desired.
例如,假设正在制作一个名为 a.js
的模块:
¥For example, suppose we were making a module called a.js
:
const EventEmitter = require('node:events');
module.exports = new EventEmitter();
// Do some work, and after some time emit
// the 'ready' event from the module itself.
setTimeout(() => {
module.exports.emit('ready');
}, 1000);
然后在另一个文件中可以这样做:
¥Then in another file we could do:
const a = require('./a');
a.on('ready', () => {
console.log('module "a" is ready');
});
赋值给 module.exports
必须立即完成。不能在任何回调中完成。以下不起作用:
¥Assignment to module.exports
must be done immediately. It cannot be
done in any callbacks. This does not work:
x.js
:
setTimeout(() => {
module.exports = { a: 'hello' };
}, 0);
y.js
:
const x = require('./x');
console.log(x.a);
exports
快捷方式#
¥exports
shortcut
exports
变量在模块的文件级作用域内可用,并在评估模块之前被分配 module.exports
的值。
¥The exports
variable is available within a module's file-level scope, and is
assigned the value of module.exports
before the module is evaluated.
它允许一个捷径,使 module.exports.f = ...
可以更简洁地写成 exports.f = ...
。但是,请注意,与任何变量一样,如果将新值分配给 exports
,则它不再绑定到 module.exports
:
¥It allows a shortcut, so that module.exports.f = ...
can be written more
succinctly as exports.f = ...
. However, be aware that like any variable, if a
new value is assigned to exports
, it is no longer bound to module.exports
:
module.exports.hello = true; // Exported from require of module
exports = { hello: false }; // Not exported, only available in the module
当 module.exports
属性被新对象完全替换时,通常也会重新分配 exports
:
¥When the module.exports
property is being completely replaced by a new
object, it is common to also reassign exports
:
module.exports = exports = function Constructor() {
// ... etc.
};
为了阐明该行为,想象一下 require()
的这个假设实现,它与 require()
的实际实现非常相似:
¥To illustrate the behavior, imagine this hypothetical implementation of
require()
, which is quite similar to what is actually done by require()
:
function require(/* ... */) {
const module = { exports: {} };
((module, exports) => {
// Module code here. In this example, define a function.
function someFunc() {}
exports = someFunc;
// At this point, exports is no longer a shortcut to module.exports, and
// this module will still export an empty default object.
module.exports = someFunc;
// At this point, the module will now export someFunc, instead of the
// default object.
})(module, module.exports);
return module.exports;
}
module.filename
#
模块的完全解析文件名。
¥The fully resolved filename of the module.
module.id
#
模块的标识符。通常这是完全解析的文件名。
¥The identifier for the module. Typically this is the fully resolved filename.
module.isPreloading
#
-
类型:<boolean> 如果模块在 Node.js 预加载阶段运行,则为
true
。¥Type: <boolean>
true
if the module is running during the Node.js preload phase.
module.loaded
#
模块是否已完成加载,或正在加载。
¥Whether or not the module is done loading, or is in the process of loading.
module.parent
#
require.main
和 module.children
。¥Stability: 0 - Deprecated: Please use require.main
and
module.children
instead.
第一个需要此模块的模块,如果当前模块是当前进程的入口点,则为 null
;如果该模块是由非 CommonJS 模块加载的模块(例如:REPL 或 import
),则为 undefined
。
¥The module that first required this one, or null
if the current module is the
entry point of the current process, or undefined
if the module was loaded by
something that is not a CommonJS module (E.G.: REPL or import
).
module.path
#
模块的目录名称。这通常与 module.id
的 path.dirname()
相同。
¥The directory name of the module. This is usually the same as the
path.dirname()
of the module.id
.
module.paths
#
模块的搜索路径。
¥The search paths for the module.
module.require(id)
#
module.require()
方法提供了一种加载模块的方法,就像从原始模块调用 require()
一样。
¥The module.require()
method provides a way to load a module as if
require()
was called from the original module.
为此,必须获得对 module
对象的引用。由于 require()
返回 module.exports
,而 module
通常仅在特定模块的代码中可用,因此必须显式导出才能使用。
¥In order to do this, it is necessary to get a reference to the module
object.
Since require()
returns the module.exports
, and the module
is typically
only available within a specific module's code, it must be explicitly exported
in order to be used.
Module
对象#
¥The Module
object
此部分已移至 模块:module
核心模块。
¥This section was moved to
Modules: module
core module.
源映射 v3 支持#
¥Source map v3 support
此部分已移至 模块:module
核心模块。
¥This section was moved to
Modules: module
core module.