当文件直接从 Node.js
运行时, require.main
将设置为其 module
。
这意味着可以直接通过测试 require.main === module
来判断文件是否已经运行。
内置模块可以使用 node:
前缀来识别,在这种情况下它会绕过 require
缓存。
例如, require('node:http')
将始终返回内置的 HTTP
模块,即使存在该名称的 require.cache
条目。
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
将使用如下所示的函数包装器对其进行包装:
(function(exports, require, module, __filename, __dirname) {
// Module code actually lives in here
})
通过这样做,Node.js
实现了一些目标:
- 它将顶级变量(使用
var
、const
或let
定义)的作用域保留在模块而不是全局对象中。 - 它有助于提供一些实际上是特定的全局变量 到模块,例如:
module
和exports
对象,实现者可以使用这些对象从模块导出值。- 方便变量
__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
);和 - 满足以下条件之一:
- 该文件的扩展名为
.mjs
。 - 该文件具有
.js
扩展名,最接近的package.json
包含"type": "module"
- 该文件具有
.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
// 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
打印结果:
/*
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
// 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
打印结果:
/*
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
*/