Skip to content

当文件直接从 Node.js 运行时, require.main 将设置为其 module

这意味着可以直接通过测试 require.main === module 来判断文件是否已经运行。

require加载逻辑

内置模块可以使用 node: 前缀来识别,在这种情况下它会绕过 require 缓存。

例如, require('node:http') 将始终返回内置的 HTTP 模块,即使存在该名称的 require.cache 条目。

js
console.log(require.main === module)

// 获取调用require()时将加载的确切文件名
console.log(require.resolve('vite'))

console.log(__dirname)

console.log(__filename)

// Module对象表示 Node.js 进程启动时加载的脚本
console.log(module)

const testResult = require('./4.module.cjs')

1.module-wrapper

在执行模块的代码之前,Node.js 将使用如下所示的函数包装器对其进行包装:

js
(function(exports, require, module, __filename, __dirname) {
// Module code actually lives in here
})

通过这样做,Node.js 实现了一些目标:

  1. 它将顶级变量(使用 varconstlet 定义)的作用域保留在模块而不是全局对象中。
  2. 它有助于提供一些实际上是特定的全局变量 到模块,例如:
  • moduleexports 对象,实现者可以使用这些对象从模块导出值。
  • 方便变量 __filename__dirname ,包含模块的绝对文件名和目录路径。

2.require-esm

https://nodejs.org/docs/latest/api/modules.html#loading-ecmascript-modules-using-require

如果未使用 --experimental-require-module 标志,则使用 require() 加载 ECMAScript 模块将引发 ERR_REQUIRE_ESM 错误,用户需要使用 import() 代替。

如果启用了 --experimental-require-module ,并且 require() 加载的 ECMAScript 模块满足以下要求:

  • 该模块是完全同步的(不包含顶级 await );和
  • 满足以下条件之一:
    1. 该文件的扩展名为 .mjs
    2. 该文件具有 .js 扩展名,最接近的 package.json 包含 "type": "module"
    3. 该文件具有 .js 扩展名,最接近的 package.json 不包含 "type": "commonjs" ,并且启用了 --experimental-detect-module

require() 会将请求的模块作为 ES 模块加载,并返回模块命名空间对象。

在这种情况下,它与动态 import() 类似,但同步运行并直接返回名称空间对象。

为了与将 ES 模块转换为 CommonJS 的现有工具进行互操作,然后可以通过 require() 加载真正的 ES 模块,返回的命名空间将包含 __esModule: true 属性(如果它具有 default 导出),以便使用工具生成的代码可以识别真实 ES 模块中的默认导出。

如果命名空间已经定义了 __esModule ,则不会添加它。此属性是实验性的,将来可能会发生变化。

它只能由遵循现有生态系统约定将 ES 模块转换为 CommonJS 模块的工具使用。直接在 CommonJS 中编写的代码应避免依赖它。

如果 require() 模块包含顶级 await ,或者它 import 模块图包含顶级 await ,则将抛出 ERR_REQUIRE_ASYNC_MODULE

在这种情况下,应该使用 import() 加载异步模块。

3.circles

循环引用

3-1.commonjs

js
// 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)

// 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 打印结果:

js
/*
main starting
a starting
b starting
in b, a.done = %j', false
b done
in a, b.done = %j', true
a done
in main, a.done = %j, b.done = %j', true, true
*/

3-2.esm

js
// main.mjs
console.log('main starting')
import { done as aDone } from './a.mjs'
import { done as bDone } from './b.mjs'
console.log('in main, a.done = %j, b.done = %j', aDone, bDone)

// a.mjs
console.log('a starting')
export var done = false
import { done as bDone } from './b.mjs'
console.log('in a, b.done = %j', bDone)
done = true
console.log('a done')

// b.mjs
console.log('b starting')
export var done = false
import { done as aDone } from './a.mjs'
console.log('in b, a.done = %j', aDone)
done = true
console.log('b done')

执行 main.mjs 打印结果:

js
/*
b starting
in b, a.done = %j false
b done
a starting
in a, b.done = %j true
a done
main starting
in main, a.done = %j, b.done = %j', true, true
*/