使用 require() 加载 ECMAScript 模块


¥Loading ECMAScript modules using require()

稳定性: 1.2 - 发布候选

¥Stability: 1.2 - Release candidate

.mjs 分机保留给 ECMAScript 模块。有关哪些文件被解析为 ECMAScript 模块的更多信息,请参阅 确定模块系统 部分。

¥The .mjs extension is reserved for ECMAScript Modules. See Determining module system section for more info regarding which files are parsed as ECMAScript modules.

require() 仅支持加载满足以下要求的 ECMAScript 模块:

¥require() only supports loading ECMAScript modules that meet 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.

如果正在加载的 ES 模块符合要求,require() 可以加载它并返回 模块命名空间对象。在这种情况下,它与动态 import() 类似,但同步运行并直接返回名称空间对象。

¥If the ES Module being loaded meets the requirements, require() can load it 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 Math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2); } 
// point.mjs
export default class Point {
  constructor(x, y) { this.x = x; this.y = y; }
} 

CommonJS 模块可以使用 require() 加载它们:

¥A CommonJS module can load them with require():

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.

当 ES 模块同时包含命名导出和默认导出时,require() 返回的结果是 模块命名空间对象,它将默认导出放在 .default 属性中,类似于 import() 返回的结果。要自定义 require(esm) 应直接返回的内容,ES 模块可以使用字符串名称 "module.exports" 导出所需的值。

¥When an ES Module contains both named exports and a default export, the result returned by require() is the module namespace object, which places the default export in the .default property, similar to the results returned by import(). To customize what should be returned by require(esm) directly, the ES Module can export the desired value using the string name "module.exports".

// point.mjs
export default class Point {
  constructor(x, y) { this.x = x; this.y = y; }
}

// `distance` is lost to CommonJS consumers of this module, unless it's
// added to `Point` as a static property.
export function distance(a, b) { return Math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2); }
export { Point as 'module.exports' } 
const Point = require('./point.mjs');
console.log(Point); // [class Point]

// Named exports are lost when 'module.exports' is used
const { distance } = require('./point.mjs');
console.log(distance); // undefined 

请注意,在上面的示例中,当使用 module.exports 导出名称时,命名导出将丢失给 CommonJS 消费者。为了允许 CommonJS 消费者继续访问命名导出,模块可以确保默认导出是一个对象,其中命名导出作为属性附加到它。例如,对于上面的例子,distance 可以作为静态方法附加到默认导出 Point 类。

¥Notice in the example above, when the module.exports export name is used, named exports will be lost to CommonJS consumers. To allow CommonJS consumers to continue accessing named exports, the module can make sure that the default export is an object with the named exports attached to it as properties. For example with the example above, distance can be attached to the default export, the Point class, as a static method.

export function distance(a, b) { return Math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2); }

export default class Point {
  constructor(x, y) { this.x = x; this.y = y; }
  static distance = distance;
}

export { Point as 'module.exports' } 
const Point = require('./point.mjs');
console.log(Point); // [class Point]

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

如果 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.

使用 require() 加载 ES 模块的支持目前处于实验阶段,可以使用 --no-experimental-require-module 禁用。要打印使用此功能的位置,请使用 --trace-require-module

¥Support for loading ES modules using require() is currently experimental and can be disabled using --no-experimental-require-module. To print where this feature is used, use --trace-require-module.

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

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