Node.js v22.11.0 文档


模块:CommonJS 模块#

¥Modules: CommonJS modules

稳定性: 2 - 稳定的

¥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 封装在一个函数中(参见 模块封装器)。在此示例中,变量 PIcircle.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 parent package.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 parent package.json file doesn't contain a top-level field "type" or there is no package.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 the type 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 parent package.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 via require(), 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.mainundefined,且主模块不可达。

¥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() 函数的语义被设计为足够通用以支持合理的目录结构。诸如 dpkgrpmnpm 之类的包管理器程序有望发现无需修改即可从 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 the foo package, version 1.2.3.

  • /usr/lib/node/bar/4.3.2/foo 依赖的 bar 包的内容。

    ¥/usr/lib/node/bar/4.3.2/: Contents of the bar package that foo 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 that bar 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()

稳定性: 1.1 - 积极开发。使用 --experimental-require-module CLI 标志启用此 API。

¥Stability: 1.1 - Active Development. Enable this API with the --experimental-require-module CLI flag.

.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:

  • 该模块是完全同步的(不包含顶层 await);and

    ¥The module is fully synchronous (contains no top-level await); and

  • 满足以下条件之一:

    ¥One of these conditions are met:

    1. 该文件的扩展名为 .mjs

      ¥The file has a .mjs extension.

    2. 该文件具有 .js 扩展名,最接近的 package.json 包含 "type": "module"

      ¥The file has a .js extension, and the closest package.json contains "type": "module"

    3. 文件具有 .js 扩展名,最接近的 package.json 不包含 "type": "commonjs",并且模块包含 ES 模块语法。

      ¥The file has a .js extension, the closest package.json does not contain "type": "commonjs", and the module contains ES module syntax.

require() 将加载请求的模块作为 ES 模块,并返回模块命名空间对象。在这种情况下,它与动态 import() 类似,但同步运行并直接返回名称空间对象。

¥require() will load the requested module as an ES Module, and return the module namespace object. In this case it is similar to dynamic import() but is run synchronously and returns the name space object directly.

使用以下 ES 模块:

¥With the following ES Modules:

// distance.mjs
export function distance(a, b) { return (b.x - a.x) ** 2 + (b.y - a.y) ** 2; } 
// point.mjs
class Point {
  constructor(x, y) { this.x = x; this.y = y; }
}
export default Point; 

CommonJS 模块可以在 --experimental-detect-module 下使用 require() 加载它们:

¥A CommonJS module can load them with require() under --experimental-detect-module:

const distance = require('./distance.mjs');
console.log(distance);
// [Module: null prototype] {
//   distance: [Function: distance]
// }

const point = require('./point.mjs');
console.log(point);
// [Module: null prototype] {
//   default: [class Point],
//   __esModule: true,
// } 

为了与将 ES 模块转换为 CommonJS 的现有工具进行互操作,然后可以通过 require() 加载真正的 ES 模块,返回的命名空间将包含 __esModule: true 属性(如果它具有 default 导出),以便使用工具生成的代码可以识别真实 ES 模块中的默认导出。如果命名空间已经定义了 __esModule,则不会添加。此属性是实验性的,将来可能会发生变化。它应该只由将 ES 模块转换为 CommonJS 模块的工具使用,遵循现有的生态系统惯例。直接用 CommonJS 编写的代码应避免依赖它。

¥For interoperability with existing tools that convert ES Modules into CommonJS, which could then load real ES Modules through require(), the returned namespace would contain a __esModule: true property if it has a default export so that consuming code generated by tools can recognize the default exports in real ES Modules. If the namespace already defines __esModule, this would not be added. This property is experimental and can change in the future. It should only be used by tools converting ES modules into CommonJS modules, following existing ecosystem conventions. Code authored directly in CommonJS should avoid depending on it.

如果 require() 的模块包含顶层 await,或者 import 的模块图包含顶层 await,则将抛出 ERR_REQUIRE_ASYNC_MODULE。在这种情况下,用户应该使用 import() 加载异步模块。

¥If the module being require()'d contains top-level await, or the module graph it imports 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.

可以通过检查 process.features.require_module 是否为 true 来检测此功能。

¥This feature can be detected by checking if process.features.require_module is true.

全部一起#

¥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"

MAYBE_DETECT_AND_LOAD(X)
1. If X parses as a CommonJS module, load X as a CommonJS module. STOP.
2. Else, if `--experimental-require-module` is
  enabled, and the source code of X can be parsed as ECMAScript module using
  <a href="esm.md#resolver-algorithm-specification">DETECT_MODULE_SYNTAX defined in
  the ESM resolver</a>,
  a. Load X as an ECMAScript module. STOP.
3. THROW the SyntaxError from attempting to parse X as CommonJS in 1. STOP.

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
      1. MAYBE_DETECT_AND_LOAD(X.js)
    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. If the "type" field is "commonjs", load X.js as an CommonJS module. STOP.
    d. MAYBE_DETECT_AND_LOAD(X.js)
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

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", GOTO d.
   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. If `--experimental-require-module` is enabled
  a. let CONDITIONS = ["node", "require", "module-sync"]
  b. Else, let CONDITIONS = ["node", "require"]
5. let MATCH = PACKAGE_IMPORTS_RESOLVE(X, pathToFileURL(SCOPE),
  CONDITIONS) <a href="esm.md#resolver-algorithm-specification">defined in the ESM resolver</a>.
6. 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. If `--experimental-require-module` is enabled
  a. let CONDITIONS = ["node", "require", "module-sync"]
  b. Else, let CONDITIONS = ["node", "require"]
6. let MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(DIR/NAME), "." + SUBPATH,
   `package.json` "exports", CONDITIONS) <a href="esm.md#resolver-algorithm-specification">defined in the ESM resolver</a>.
7. 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

稳定性: 3 - 旧版:请改用 子路径导出子路径导入

¥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.jsindex.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 错误。使用包 子路径导出子路径导入 可以提供与文件夹作为模块相同的包含组织优势,并且适用于 requireimport

¥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:

  • 它将顶层变量(使用 varconstlet 定义)保持在模块而不是全局对象的范围内。

    ¥It keeps top-level variables (defined with var, const, or let) 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:

    • moduleexports 对象,实现者可以用来从模块中导出值。

      ¥The module and exports 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#

当前模块的目录名。这与 __filenamepath.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 

给定两个模块:ab,其中 ba 的依赖,目录结构为:

¥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#

稳定性: 0 - 已弃用

¥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 the node_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 字符串引用核心模块,例如 httpfs,则返回包含在解析 requestnull 期间搜索的路径的数组。

¥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#

稳定性: 0 - 已弃用:请改用 require.mainmodule.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.idpath.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)#

  • id <string>

  • 返回:<any> 导出的模块内容

    ¥Returns: <any> exported module content

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.

Node.js 中文网 - 粤ICP备13048890号