CommonJS 是 Node 模块化 JS 代码的原始方式。
导入和导出的例子:
const circle = require('./circle.js');
console.log(`The area of a circle of radius 4 is ${circle.area(4)}`);
const { PI } = Math;
exports.area = (r) => PI * r ** 2;
exports.circumference = (r) => 2 * PI * r;
模块会被 Node 包裹在一个函数里,因此模块里的本地变量会是私有的。比如例子中的 PI
。
可以直接给 module.exports
赋新值,可以是个函数或者类,等等。
启用方式
默认情况下,以下情形 Node 会视为 CommonJS 模块:
- 扩展名为
.cjs
的文件; - 扩展名为
.js
的文件,且离文件最近的package.json
里指定了"type": "commonjs"
; - 扩展名为
.js
或者没有扩展名的文件,且最近的package.json
不存在"type"
字段,或者没有package.json
文件;或者当文件被作为 ES Modules 解析时出现了语法错误。包作者应当明确"type"
字段,即便所有的代码都是 CommonJS 模块。 - 扩展名不为
.mjs
、.cjs
、.json
、.node
、.js
的文件(当最近的package.json
指定了"type": "module"
时:如果文件是通过require()
导入的,则会视为 CommonJS 模块;如果是作为了程序的主入口,反之)。
See Determining module system for more details.
调用 require()
会使用 CommonJS 模块加载器。而调用 import()
会使用 ES Modules 加载器。
获取主模块
当一个文件被直接用 Node 运行,require.main
会被设置为该文件的 module
。因此可以用 require.main === module
判断当前文件是否是被直接运行。
比如对于 foo.js
,如果通过 node foo.js
运行结果会是 true
;但如果通过 require('./foo')
引入,结果会是 false
。
如果入口文件不是 CommonJS 模块,require.main
将会是 undefined
,且没有别的办法获取主模块。
.mjs
扩展
因为 require()
是同步的,所以不能用于加载 ES Modules 。应当使用 import()
。
同样的,.mjs
文件也不能用 require()
导入,因为 .mjs
本身会被 Node 视为 ES Modules 。
总览
如果想知道 require()
具体引入哪个文件,可以用 require.resolve()
获取。
若在路径为 Y 的模块中调用 require(X)
,require()
的执行逻辑如下:
- 如果 X 是核心模块,则返回该核心模块
- 如果 X 以 '/' 开头,则以 Y 作为文件跟路径
- 如果 X 以 './' 或 '/' 或 '../' 开头,执行:
- LOAD_AS_FILE(Y + X)
- LOAD_AS_DIRECTORY(Y + X)
- 抛出错误 "not found"
- 如果 X 以 '#' 开头,执行:
- LOAD_PACKAGE_IMPORTS(X, dirname(Y))
- 执行 LOAD_PACKAGE_SELF(X, dirname(Y))
- 执行 LOAD_NODE_MODULES(X, dirname(Y))
- 抛出错误 "not found"
更多见 All togeter 。
缓存
模块会在第一次加载后被缓存起来。在后续的同文件的 require()
调用会得到相同的模块对象。
在面对模块之间的循环引用时,Node 会先返回一个未完成的模块对象。
如果想要让一个模块里的代码重复执行几次,可以将其作为函数导出,通过调用函数来进行重复执行。
模块缓存的使用陷阱
模块缓存是基于当初被解析的具体文件名。然而一个模块名可能会因为调用位置不同,分别被解析到不同的文件,所以并不会 保证 require('foo')
永远返回相同的对象。
除此之外,Node 会区分模块名以及所解析的文件名的大小写,即便是在一些不区分文件大小写的操作系统上。
核心模块
Node 内置了一些模块,它们被编译为了二进制文件。这些核心模块放在了 lib/
目录里。
核心模块可使用 node:
前缀作为标识,它会绕过 require
的缓存机制。比如 require('node:http')
会直接返回 HTTP 模块,而不是从 require.cache
获取模块引用。
当传入给 require()
的模块标识与一些内置核心模块重名时,会直接取用加载核心模块。比如对于 require('http')
永远会返回内置的 HTTP 模块,即便有这么一个同名的模块文件。这些内置的核心模块也可以通过 module.builtinModules
访问(前提是没有用 node:
前缀)。
循环引用
当碰到循环 require()
调用时,模块可能还未执行完毕就被返回了。考虑这种情形:
// `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
模块。最后的输出结果如下:
$ 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
如果应用中需要循环引用模块,应当小心使用。
文件模块
如果未找到给定的文件名,Node 会依次尝试给文件名添加 .js
、.json
、.node
再次查找。如果需要导入一个 .cjs
文件,则需要 在 require()
里指定文件的完整名称,比如 require('./file.cjs')
。
-
.json
会被解析为 JSON 对象; -
.node
会被视为 addon 模块,并用process.dlopen()
加载; -
其他扩展名的文件会被解析为 JS 代码文件。
-
如果模块名是以
'/'
作为前缀,则代表绝对路径。比如require('/home/marco/foo.js')
会加载/home/marco/foo.js
文件。 -
如果模块名是以
'./'
作为前缀,则其路径会相对于引用该模块的文件的路径。比如在foo.js
中如果要调用require('./circle')
,则需要两文件在同一个目录中。 -
如果模块名没有
'/'
、'./'
、'../'
前缀,则必须是个核心模块,或着是一个node_modules
文件夹。 -
如果根据模块名最终未找到对应的文件,
require()
会抛出一个MODULE_NOT_FOUND
错误。
文件夹作为模块
Stability: 3 - Legacy: Use subpath exports or subpath imports instead.
有三种方法可以将文件夹作为 require()
参数。
第一种方法则是在文件夹中创建一个 package.json
文件,并且指定 "main"
来表明入口。下面是个例子:
{ "name" : "some-library",
"main" : "./lib/some-library.js" }
如果文件夹为 ./some-library
,若调用 require('./some-library')
会尝试加载 ./some-library/lib/some-library.js
文件。
如果文件夹中的 package.json
未指定 "main"
,或者甚至没有 package.json
,则 Node 会尝试在文件夹中寻找 index.js
或 index.node
文件。如果仍未找到,Node 会报错:
Error: Cannot find module 'some-library'
在所有三种方法中,如果调用 import('./some-library')
都会导致一个 ERR_UNSUPPORTED_DIR_IMPORT
。使用 package 的 subpath exports 及 subpath imports 则可以提供相同的文件组织结构来将文件夹作为模块,并可用于 require
或 import
导入。
从 node_modules
加载模块
如果传给 require()
的模块名并不是核心模块,并且不以 '/'
、'../'
、'./'
开头,则 Node 会从当前模块所在的目录下寻找 /node_modules
,并从中寻找模块。如果未找到,则再向上级文件夹寻找。
模块名里面也可带上具体的路径,来表示引入模块中某个具体的文件。比如 require('example-module/path/to/file')
会在 example-module
中寻找路径为 path/to/file
的文件。
Loading from the global folders
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.
On Windows, NODE_PATH
is delimited by semicolons (;
) instead of colons.
NODE_PATH
was originally created to support loading modules from
varying paths before the current module resolution algorithm was defined.
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.
Additionally, Node.js will search in the following list of GLOBAL_FOLDERS:
- 1:
$HOME/.node_modules
- 2:
$HOME/.node_libraries
- 3:
$PREFIX/lib/node
Where $HOME
is the user's home directory, and $PREFIX
is the Node.js
configured node_prefix
.
These are mostly for historic reasons.
It is strongly encouraged to place dependencies in the local node_modules
folder. These will be loaded faster, and more reliably.
模块包装器
在执行一个模块的代码之前,Node 会将其包装在一个函数里:
(function(exports, require, module, __filename, __dirname) {
// Module code actually lives in here
});
这样做的目的有:
- 能够保证模块里的顶层变量不暴露在全局对象上;
- 提供一些专属于模块的类全局变量,比如:
- 用于导出的
module
及exports
对象; - 标识模块文件位置的变量,比如
__filename
、__dirname
。
- 用于导出的
The module scope
__dirname
- {string}
The directory name of the current module. This is the same as the
path.dirname()
of the __filename
.
Example: running node example.js
from /Users/mjr
console.log(__dirname);
// Prints: /Users/mjr
console.log(path.dirname(__filename));
// Prints: /Users/mjr
__filename
- {string}
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.
See __dirname
for the directory name of the current module.
Examples:
Running node example.js
from /Users/mjr
console.log(__filename);
// Prints: /Users/mjr/example.js
console.log(__dirname);
// Prints: /Users/mjr
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
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
- {Object}
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}
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} module name or path- Returns: {any} exported module content
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
- {Object}
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.
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.main
- {module | 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".
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} The module path to resolve.options
{Object}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.
- Returns: {string}
Use the internal require()
machinery to look up the location of a module,
but rather than loading the module, just return the resolved filename.
If the module can not be found, a MODULE_NOT_FOUND
error is thrown.
require.resolve.paths(request)
request
{string} The module path whose lookup paths are being retrieved.- Returns: {string[]|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
.
The module
object
- {Object}
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
- {module[]}
The module objects required for the first time by this one.
module.exports
- {Object}
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.
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');
});
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
shortcut
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.
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
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.
};
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
- {string}
The fully resolved filename of the module.
module.id
- {string}
The identifier for the module. Typically this is the fully resolved filename.
module.isPreloading
- Type: {boolean}
true
if the module is running during the Node.js preload phase.
module.loaded
- {boolean}
Whether or not the module is done loading, or is in the process of loading.
module.parent
Stability: 0 - Deprecated: Please use
require.main
andmodule.children
instead.
module.path
- {string}
The directory name of the module. This is usually the same as the
path.dirname()
of the module.id
.
module.paths
- {string[]}
The search paths for the module.
module.require(id)
id
{string}- Returns: {any} exported module content
The module.require()
method provides a way to load a module as if
require()
was called from the original 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.
The Module
object
This section was moved to
Modules: module
core module.
Source map v3 support
This section was moved to
Modules: module
core module.