Модули: модули CommonJS
Стабильность: 2 — Стабильный
Модули CommonJS — это исходный способ упаковки кода JavaScript для Node.js. Node.js также поддерживает стандарт модулей ECMAScript, используемый браузерами и другими средами выполнения JavaScript.
В Node.js каждый файл рассматривается как отдельный модуль. Например, рассмотрим файл с именем foo.js:
const circle = require('./circle.js');
console.log(`The area of a circle of radius 4 is ${circle.area(4)}`);
jscopy
В первой строке foo.js загружает модуль circle.js, находящийся в том же каталоге, что и foo.js.
Вот содержимое circle.js:
const { PI } = Math;
exports.area = (r) => PI * r ** 2;
exports.circumference = (r) => 2 * PI * r;
jscopy
Модуль circle.js экспортировал функции area() и circumference(). Функции и объекты добавляются в корень модуля путем указания дополнительных свойств у специального объекта exports.
Переменные, локальные для модуля, будут приватными, поскольку Node.js оборачивает модуль в функцию (см. обертку модуля). В этом примере переменная PI является приватной для circle.js.
Свойству module.exports можно присвоить новое значение (например, функцию или объект).
В следующем коде bar.js использует модуль square, который экспортирует класс Square:
const Square = require('./square.js');
const mySquare = new Square(2);
console.log(`The area of mySquare is ${mySquare.area()}`);
jscopy
Модуль square определен в 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;
}
};
jscopy
Система модулей CommonJS реализована в основном модуле module.
Включение
В Node.js используются две системы модулей: модули CommonJS и модули ECMAScript.
По умолчанию Node.js рассматривает следующие файлы как модули CommonJS:
-
Файлы с расширением
.cjs. -
Файлы с расширением
.jsили без расширения, если ближайший родительский файлpackage.jsonсодержит поле верхнего уровня"type"со значением"commonjs". -
Файлы с расширением
.jsили без расширения, если ближайший родительский файлpackage.jsonне содержит поля верхнего уровня"type"или если в родительских каталогах нет файлаpackage.json; за исключением случаев, когда файл содержит синтаксис, приводящий к ошибке, если он интерпретируется не как модуль ES. Авторам пакетов следует включать поле"type", даже в пакетах, исходный код которых целиком состоит из CommonJS. Явное указаниеtypeпакета упростит средствам сборки и загрузчикам определение того, как следует интерпретировать файлы в пакете. -
Файлы с расширением, отличным от
.mjs,.cjs,.json,.nodeили.js, если ближайший родительский файлpackage.jsonсодержит поле верхнего уровня"type"со значением"module".
Подробнее см. раздел Определение системы модулей.
Вызов require() всегда использует загрузчик модулей CommonJS. Вызов import() всегда использует загрузчик модулей ECMAScript.
Доступ к главному модулю
Когда файл запускается непосредственно из Node.js, require.main получает значение module. Это позволяет определить, был ли файл запущен непосредственно, проверив require.main === module.
Для файла foo.js это будет true при запуске через node foo.js, но false при запуске с помощью require('./foo').
Если точка входа не является модулем CommonJS, require.main имеет значение undefined, и доступ к главному модулю невозможен.
Советы по менеджерам пакетов
Семантика функции require() в Node.js разработана достаточно универсально, чтобы поддерживать разумную структуру каталогов. Надеемся, что программы для управления пакетами, такие как dpkg, rpm и npm, смогут собирать из модулей Node.js нативные пакеты без изменений.
Ниже мы предлагаем структуру каталогов, которая может подойти:
Допустим, мы хотим, чтобы каталог /usr/lib/node/<some-package>/<some-version> содержал файлы определенной версии пакета.
Пакеты могут зависеть друг от друга. Для установки пакета foo может потребоваться установить определенную версию пакета bar. Сам пакет bar может иметь зависимости, которые в некоторых случаях могут конфликтовать или образовывать циклические зависимости.
Поскольку Node.js ищет realpath любых загружаемых модулей (то есть разрешает символические ссылки), а затем ищет их зависимости в каталогах node_modules, эту ситуацию можно разрешить с помощью следующей архитектуры:
-
/usr/lib/node/foo/1.2.3/: содержимое пакетаfooверсии 1.2.3. -
/usr/lib/node/bar/4.3.2/: содержимое пакетаbar, от которого зависитfoo. -
/usr/lib/node/foo/1.2.3/node_modules/bar: символическая ссылка на/usr/lib/node/bar/4.3.2/. -
/usr/lib/node/bar/4.3.2/node_modules/*: символические ссылки на пакеты, от которых зависитbar.
Таким образом, даже если возникнет цикл или конфликты зависимостей, каждый модуль сможет получить версию своей зависимости, которую он может использовать.
Когда код пакета 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.
Кроме того, чтобы сделать процесс поиска модулей еще более эффективным, вместо размещения пакетов непосредственно в /usr/lib/node можно поместить их в /usr/lib/node_modules/<name>/<version>. Тогда Node.js не будет искать отсутствующие зависимости в /usr/node_modules или /node_modules.
Чтобы модули были доступны в REPL Node.js, может быть полезно также добавить каталог /usr/lib/node_modules в переменную окружения $NODE_PATH. Поскольку поиск модулей в каталогах node_modules выполняется по относительным путям, основанным на реальном пути файлов, из которых вызывается require(), сами пакеты могут находиться где угодно.
Загрузка модулей ECMAScript с помощью require()
Расширение .mjs зарезервировано для модулей ECMAScript. Подробнее о том, какие файлы разбираются как модули ECMAScript, см. в разделе Определение системы модулей.
require() поддерживает загрузку только тех модулей ECMAScript, которые соответствуют следующим требованиям:
- Модуль полностью синхронный (не содержит
awaitверхнего уровня); и - Выполняется одно из следующих условий:
- Файл имеет расширение
.mjs. - Файл имеет расширение
.js, а в ближайшемpackage.jsonсодержится"type": "module" - Файл имеет расширение
.js, ближайшийpackage.jsonне содержит"type": "commonjs", а модуль содержит синтаксис модуля ES.
- Файл имеет расширение
Если загружаемый модуль ES соответствует этим требованиям, require() может загрузить его и вернуть объект пространства имен модуля. В этом случае он аналогичен динамическому import(), но выполняется синхронно и возвращает объект пространства имен напрямую.
Имеются следующие модули ES:
// distance.mjs
export function distance(a, b) { return Math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2); }
mjscopy
// point.mjs
export default class Point {
constructor(x, y) { this.x = x; this.y = y; }
}
mjscopy
Модуль CommonJS может загрузить их с помощью 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,
// }
cjscopy
Для совместимости с существующими инструментами, преобразующими модули ES в CommonJS, которые затем могли бы загружать настоящие модули ES через require(), возвращаемое пространство имен будет содержать свойство __esModule: true, если у него есть экспорт default. Это позволит коду, сгенерированному инструментами, распознавать экспорты по умолчанию в настоящих модулях ES. Если пространство имен уже определяет __esModule, это свойство добавлено не будет. Это свойство является экспериментальным и может измениться в будущем. Его следует использовать только инструментам, преобразующим модули ES в модули CommonJS, в соответствии с существующими соглашениями экосистемы. Код, написанный непосредственно на CommonJS, не должен от него зависеть.
Результатом require() является объект пространства имен модуля, в котором экспорт по умолчанию находится в свойстве .default, как и в результатах, возвращаемых import(). Чтобы настроить значение, которое должно напрямую возвращаться require(esm), модуль ES может экспортировать нужное значение под строковым именем "module.exports".
Модули JavaScript
// 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' };
CommonJS
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.
Модули JavaScript
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' };
CommonJS
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().
Если включен --experimental-print-required-tla, Node.js не выбросит ERR_REQUIRE_ASYNC_MODULE до начала вычисления, а вычислит модуль, попытается найти операторы await верхнего уровня и выведет их расположение, чтобы помочь пользователям устранить проблему.
Если поддержка загрузки модулей ES с помощью require() вызывает непредвиденные проблемы, ее можно отключить с помощью --no-require-module. Чтобы вывести сведения о местах использования этой функции, используйте --trace-require-module.
Наличие этой функции можно проверить, убедившись, что process.features.require_module имеет значение true.
Все вместе
Чтобы получить точное имя файла, который будет загружен при вызове require(), используйте функцию require.resolve().
Обобщая все вышесказанное, ниже приведен высокоуровневый алгоритм работы require() в виде псевдокода:
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 the file system root
3. If X is equal to '.', or X begins with './', '/' 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 the source code of X can be parsed as ECMAScript module using
DETECT_MODULE_SYNTAX defined in the ESM resolver,
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 a 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 a 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 = DIRS + DIR
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 `--no-require-module` is not enabled
a. let CONDITIONS = ["node", "require", "module-sync"]
b. Else, let CONDITIONS = ["node", "require"]
5. let MATCH = PACKAGE_IMPORTS_RESOLVE(X, pathToFileURL(SCOPE),
CONDITIONS) defined in the ESM resolver.
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 `--no-require-module` is not 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) defined in the ESM resolver.
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"])
defined in the ESM resolver.
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"
textcopy
«Разрешатель ESM» описан в документации по ESM.
Кэширование
Модули кэшируются после первой загрузки. Это означает, в частности, что при каждом вызове require('foo') будет возвращаться один и тот же объект, если вызовы разрешаются в один и тот же файл.
Если require.cache не изменяется, многократные вызовы require('foo') не приведут к повторному выполнению кода модуля. Это важная возможность. Благодаря ей могут возвращаться «частично созданные» объекты, что позволяет загружать транзитивные зависимости, даже если это приводит к циклам.
Чтобы код модуля выполнялся несколько раз, экспортируйте функцию и вызывайте ее.
Особенности кэширования модулей
Модули кэшируются по разрешенному имени файла. Поскольку модуль может разрешаться в разные имена файлов в зависимости от расположения вызывающего модуля (при загрузке из каталогов node_modules), нет гарантии, что require('foo') всегда вернет один и тот же объект, если вызовы разрешаются в разные файлы.
Кроме того, в файловых системах и операционных системах без учета регистра разные разрешенные имена файлов могут указывать на один и тот же файл, однако кэш будет считать их разными модулями и загружать файл несколько раз. Например, require('./foo') и require('./FOO') возвращают два разных объекта независимо от того, являются ли ./foo и ./FOO одним и тем же файлом.
Встроенные модули
В двоичный файл Node.js скомпилировано несколько модулей. Эти модули подробнее описаны в других разделах документации.
Встроенные модули определены в исходном коде Node.js и находятся в каталоге lib/.
Встроенные модули можно идентифицировать по префиксу node:; в этом случае кэш require не используется. Например, require('node:http') всегда вернет встроенный модуль HTTP, даже если существует запись require.cache с таким именем.
Некоторые встроенные модули всегда загружаются в приоритетном порядке, если их идентификатор передан в require(). Например, require('http') всегда вернет встроенный модуль HTTP, даже если существует файл с таким именем.
Список всех встроенных модулей можно получить из module.builtinModules. Все модули в этом списке указаны без префикса node:, за исключением тех, для которых такой префикс обязателен (как объясняется в следующем разделе).
Встроенные модули с обязательным префиксом node:
При загрузке с помощью require() некоторые встроенные модули необходимо запрашивать с префиксом node:. Это требование предотвращает конфликты новых встроенных модулей с пакетами, созданными сторонними разработчиками и уже использующими такие имена. В настоящее время к встроенным модулям, для которых требуется префикс node:, относятся:
Список этих модулей с учетом префикса доступен в module.builtinModules.
Циклические зависимости
При наличии циклических вызовов 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');
jscopy
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');
jscopy
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);
jscopy
Когда main.js загружает a.js, тот, в свою очередь, загружает b.js с помощью a.js. В этот момент b.js пытается загрузить a.js. Чтобы предотвратить бесконечный цикл, модулю b.js возвращается незавершенная копия объекта exports модуля a.js. Затем b.js завершает загрузку, и его объект exports передается модулю a.js.
К моменту загрузки main.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 consolecopy
Для корректной работы циклических зависимостей модулей в приложении требуется тщательное планирование.
Файловые модули
Если файл с точным именем не найден, Node.js попытается загрузить файл, добавляя к его имени расширения .js, .json и, наконец, .node. При загрузке файла с другим расширением (например, .cjs) в require() необходимо передать его полное имя, включая расширение (например, require('./file.cjs')).
Файлы .json разбираются как текстовые файлы JSON, а файлы .node интерпретируются как скомпилированные модули-дополнения, загружаемые с помощью process.dlopen(). Файлы с любым другим расширением (или без расширения) разбираются как текстовые файлы JavaScript. О том, какая цель разбора будет использоваться, см. в разделе Определение системы модулей.
Если перед именем требуемого модуля стоит '/', это абсолютный путь к файлу. Например, require('/home/marco/foo.js') загрузит файл по пути /home/marco/foo.js.
Если перед именем требуемого модуля стоит './', путь указывается относительно файла, вызывающего require(). То есть circle.js должен находиться в том же каталоге, что и foo.js, чтобы require('./circle') мог его найти.
Если перед именем файла нет начальных '/', './' или '../', модуль должен быть основным модулем или загружаться из каталога node_modules.
Если указанный путь не существует, require() выбросит ошибку MODULE_NOT_FOUND.
Каталоги как модули
Стабильность: 3 — Устаревшее: вместо этого используйте экспорты подпутей или импорты подпутей.
Передать каталог в require() в качестве аргумента можно тремя способами.
Первый способ — создать файл package.json в корне каталога, указав в нем модуль main. Пример файла package.json может выглядеть так:
{ "name" : "some-library",
"main" : "./lib/some-library.js" }
jsoncopy
Если бы этот файл находился в каталоге ./some-library, то require('./some-library') попытался бы загрузить ./some-library/lib/some-library.js.
Если в каталоге нет файла package.json или запись "main" отсутствует либо не может быть разрешена, Node.js попытается загрузить из этого каталога файл index.js или index.node. Например, если в предыдущем примере файла package.json нет, то require('./some-library') попытается загрузить:
./some-library/index.js./some-library/index.node
Если эти попытки завершатся неудачей, Node.js сообщит, что весь модуль не найден, используя ошибку по умолчанию:
Error: Cannot find module 'some-library' consolecopy
Во всех трех описанных случаях вызов import('./some-library') приведет к ошибке ERR_UNSUPPORTED_DIR_IMPORT. Экспорты подпутей или импорты подпутей пакетов обеспечивают те же преимущества организации и изоляции, что и каталоги-модули, и работают как с require, так и с import.
Загрузка из каталогов node_modules
Если идентификатор модуля, переданный в require(), не является встроенным модулем и не начинается с '/', '../' или './', Node.js начинает поиск в каталоге текущего модуля, добавляет /node_modules и пытается загрузить модуль из этого расположения. Node.js не будет добавлять node_modules к пути, который уже заканчивается на node_modules.
Если модуль там не найден, Node.js переходит в родительский каталог и продолжает поиск, пока не достигнет корня файловой системы.
Например, если файл '/home/ry/projects/foo.js' вызывает require('bar.js'), Node.js будет искать модуль в следующих местах, в таком порядке:
/home/ry/projects/node_modules/bar.js/home/ry/node_modules/bar.js/home/node_modules/bar.js/node_modules/bar.js
Это позволяет программам размещать зависимости локально, чтобы они не конфликтовали друг с другом.
Можно запросить конкретные файлы или подмодули, распространяемые вместе с модулем, добавив суффикс пути после имени модуля. Например, require('example-module/path/to/file') разрешится в path/to/file относительно расположения example-module. Для пути с суффиксом действуют те же правила разрешения модулей.
Загрузка из глобальных каталогов
Если переменной окружения NODE_PATH присвоен список абсолютных путей, разделенных двоеточиями, Node.js будет искать модули по этим путям, если они не найдены в других местах.
В Windows значения NODE_PATH разделяются точками с запятой (;), а не двоеточиями.
Изначально NODE_PATH была создана для поддержки загрузки модулей из разных путей до того, как был определен современный алгоритм разрешения модулей.
Поддержка NODE_PATH сохраняется, но теперь она менее необходима, поскольку в экосистеме Node.js установилось соглашение о расположении зависимых модулей. Иногда развертывания, использующие NODE_PATH, ведут себя неожиданно, когда пользователи не знают, что необходимо задать NODE_PATH. Иногда зависимости модуля изменяются, из-за чего при поиске NODE_PATH загружается другая версия (или даже другой модуль).
Кроме того, Node.js ищет модули в следующем списке ГЛОБАЛЬНЫХ_КАТАЛОГОВ:
- 1:
$HOME/.node_modules - 2:
$HOME/.node_libraries - 3:
$PREFIX/lib/node
Здесь $HOME — домашний каталог пользователя, а $PREFIX — настроенный для Node.js каталог node_prefix.
В основном это используется по историческим причинам.
Настоятельно рекомендуется размещать зависимости в локальном каталоге node_modules. Они будут загружаться быстрее и надежнее.
Обертка модуля
Перед выполнением кода модуля Node.js оборачивает его в функцию, которая выглядит следующим образом:
(function(exports, require, module, __filename, __dirname) {
// Module code actually lives in here
});
jscopy
Это позволяет Node.js добиться нескольких целей:
- Переменные верхнего уровня (объявленные с помощью
var,constилиlet) имеют область видимости модуля, а не глобального объекта. - Предоставить переменные, которые выглядят глобальными, но фактически относятся к конкретному модулю, например:
- Объекты
moduleиexports, которые реализующий модуль код может использовать для экспорта значений. - Вспомогательные переменные
__filenameи__dirname, содержащие абсолютное имя файла и путь к каталогу модуля.
- Объекты
Область видимости модуля
__dirname
- Тип:
<string>
Имя каталога текущего модуля. Оно совпадает с результатом вызова path.dirname() для __filename.
Пример: запуск node example.js из /Users/mjr
console.log(__dirname); // Prints: /Users/mjr console.log(path.dirname(__filename)); // Prints: /Users/mjr jscopy
__filename
- Тип:
<string>
Имя файла текущего модуля. Это абсолютный путь к файлу текущего модуля с разрешенными символическими ссылками.
Для главной программы это имя не обязательно совпадает с именем файла, указанным в командной строке.
Имя каталога текущего модуля см. в разделе __dirname.
Примеры:
Запуск node example.js из /Users/mjr
console.log(__filename); // Prints: /Users/mjr/example.js console.log(__dirname); // Prints: /Users/mjr jscopy
Пусть имеются два модуля: a и b, где b является зависимостью a, а структура каталогов выглядит так:
/Users/mjr/app/a.js/Users/mjr/app/node_modules/b/b.js
Ссылки на __filename внутри b.js будут возвращать /Users/mjr/app/node_modules/b/b.js, тогда как ссылки на __filename внутри a.js будут возвращать /Users/mjr/app/a.js.
exports
- Тип:
<Object>
Ссылка на module.exports, которую короче записывать. Подробности о том, когда использовать exports, а когда module.exports, см. в разделе о сокращенной записи exports.
module
- Тип:
<module>
Ссылка на текущий модуль. См. раздел об объекте module. В частности, module.exports используется для определения экспорта модуля, доступного через require().
require(id)
Используется для импорта модулей, JSON и локальных файлов. Модули можно импортировать из node_modules. Локальные модули и файлы JSON можно импортировать по относительному пути (например, ./, ./foo, ./bar/baz, ../foo), который будет разрешен относительно каталога, указанного в __dirname (если он определен), или относительно текущего рабочего каталога. Относительные пути в стиле POSIX разрешаются независимо от операционной системы, поэтому приведенные выше примеры будут работать в Windows так же, как в Unix-системах.
// 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');
jscopy
require.cache
- Тип:
<Object>
При подключении модули кэшируются в этом объекте. Удалив из него значение ключа, можно добиться того, что при следующем вызове require модуль загрузится заново. Это не относится к нативным дополнениям, повторная загрузка которых приведет к ошибке.
Также можно добавлять или заменять записи. Этот кэш проверяется перед встроенными модулями; если в кэш добавить имя, совпадающее с именем встроенного модуля, встроенный модуль будет возвращаться только при вызове require с префиксом node:. Используйте с осторожностью!
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);
jscopy
require.extensions
Стабильность: 0 — устарело
- Тип:
<Object>
Указывает require, как обрабатывать определённые расширения файлов.
Обрабатывать файлы с расширением .sjs как .js:
require.extensions['.sjs'] = require.extensions['.js']; jscopy
Устарело. Ранее этот список использовался для загрузки модулей, не написанных на JavaScript, в Node.js путём их компиляции по требованию. Однако на практике существуют гораздо более эффективные способы сделать это, например загружать модули с помощью другой программы Node.js или заранее компилировать их в JavaScript.
Избегайте использования require.extensions. Это может привести к трудноуловимым ошибкам, а разрешение расширений замедляется с каждым зарегистрированным расширением.
require.main
- Тип:
<module>|<undefined>
Объект Module, представляющий входной скрипт, загруженный при запуске процесса Node.js, или undefined, если точка входа программы не является модулем CommonJS. См. раздел «Доступ к главному модулю».
В скрипте entry.js:
console.log(require.main); jscopy
node entry.js bashcopy
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' ] }
jscopy
require.resolve(request[, options])
-
request<string>Путь к модулю, который требуется разрешить. -
options<Object>-
paths<string[]>Пути, откуда следует искать расположение модуля. Если указаны, эти пути используются вместо путей разрешения по умолчанию, за исключением GLOBAL_FOLDERS, таких как$HOME/.node_modules, которые включаются всегда. Каждый из этих путей используется как начальная точка алгоритма разрешения модуля, то есть иерархияnode_modulesпроверяется начиная с этого расположения.
-
- Возвращает:
<string>
Использует внутренние механизмы require() для поиска расположения модуля, но вместо загрузки модуля просто возвращает разрешённое имя файла.
Если модуль не найден, генерируется ошибка MODULE_NOT_FOUND.
require.resolve.paths(request)
-
request<string>Путь к модулю, для которого извлекаются пути поиска. - Возвращает:
<string[]>|<null>
Возвращает массив с путями, проверенными при разрешении request, или null, если строка request ссылается на встроенный модуль, например http или fs.
Объект module
- Тип:
<Object>
В каждом модуле свободная переменная module ссылается на объект, представляющий текущий модуль. Для удобства module.exports также доступен через глобальную переменную модуля exports. На самом деле module не является глобальной переменной, а имеет локальную область видимости в каждом модуле.
module.children
- Тип:
<module[]>
Объекты модулей, впервые подключённых этим модулем.
module.exports
- Тип:
<Object>
Объект module.exports создаётся системой Module. Иногда это неприемлемо: многим требуется, чтобы их модуль был экземпляром некоторого класса. Для этого присвойте желаемый объект экспорта переменной module.exports. Присваивание желаемого объекта переменной exports просто переназначит локальную переменную exports, что, вероятно, не является желаемым поведением.
Например, предположим, что мы создаём модуль под названием 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);
jscopy
Тогда в другом файле можно написать:
const a = require('./a');
a.on('ready', () => {
console.log('module "a" is ready');
});
jscopy
Присваивание переменной module.exports необходимо выполнить немедленно. Его нельзя выполнять в обратных вызовах. Следующий вариант не работает:
x.js:
setTimeout(() => {
module.exports = { a: 'hello' };
}, 0);
jscopy
y.js:
const x = require('./x');
console.log(x.a);
jscopy
Сокращение exports
Переменная exports доступна в файловой области видимости модуля, и до выполнения модуля ей присваивается значение module.exports.
Это позволяет использовать сокращённую запись: вместо module.exports.f = ... можно написать exports.f = .... Однако следует учитывать, что, как и любой другой переменной, при присваивании нового значения переменной exports она перестаёт быть связанной с module.exports:
module.exports.hello = true; // Exported from require of module
exports = { hello: false }; // Not exported, only available in the module
jscopy
Если свойство module.exports полностью заменяется новым объектом, обычно также переназначают exports:
module.exports = exports = function Constructor() {
// ... etc.
};
jscopy
Чтобы пояснить это поведение, представьте гипотетическую реализацию require(), очень похожую на фактическую реализацию в 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;
}
jscopy
module.filename
- Тип:
<string>
Полностью разрешённое имя файла модуля.
module.id
- Тип:
<string>
Идентификатор модуля. Обычно это полностью разрешённое имя файла.
module.isPreloading
- Тип:
<boolean>true, если модуль выполняется на этапе предварительной загрузки Node.js.
module.loaded
- Тип:
<boolean>
Показывает, завершена ли загрузка модуля или она ещё выполняется.
module.parent
Стабильность: 0 — устарело: используйте вместо этого require.main и module.children.
- Тип:
<module>|<null>|<undefined>
Модуль, который первым подключил этот модуль, или null, если текущий модуль является точкой входа текущего процесса, либо undefined, если модуль загружен чем-либо, не являющимся модулем CommonJS (например, REPL или import).
module.path
- Тип:
<string>
Имя каталога модуля. Обычно оно совпадает с результатом вызова path.dirname() для module.id.
module.paths
- Тип:
<string[]>
Пути поиска модуля.
module.require(id)
Метод module.require() позволяет загрузить модуль так, как если бы require() был вызван из исходного модуля.
Для этого необходимо получить ссылку на объект module. Поскольку require() возвращает module.exports, а module обычно доступен только в коде конкретного модуля, его необходимо явно экспортировать, чтобы использовать.
Объект Module
Этот раздел перенесён в Модули: встроенный модуль module.
Поддержка Source Map v3
Этот раздел перенесён в Модули: встроенный модуль module.
© Joyent, Inc. and other Node contributors
Licensed under the MIT License.
Node.js is a trademark of Joyent, Inc. and is used with its permission.
We are not endorsed by or affiliated with Joyent.
https://nodejs.org/api/modules.html