Spec-Zone.ru › Node.js 8 LTS

Модули

Устойчивость: 2 - Стабильно

В системе модулей Node.js каждый файл рассматривается как отдельный модуль. Например, рассмотрим файл с именем foo.js:

const circle = require('./circle.js');
console.log(`The area of a circle of radius 4 is ${circle.area(4)}`);

В первой строке foo.js загружает модуль circle.js, который находится в той же директории, что и foo.js.

Вот содержимое circle.js:

const { PI } = Math;

exports.area = (r) => PI * r ** 2;

exports.circumference = (r) => 2 * PI * r;

Модуль 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()}`);

Модуль 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;
  }
};

Система модулей реализована в модуле require('module').

Доступ к главному модулю

Когда файл запускается непосредственно из Node.js, require.main устанавливается в его module значение. Это означает, что можно определить, был ли файл запущен напрямую, проверив require.main === module.

Для файла foo.js, это будет true при запуске через node foo.js, но false при запуске через require('./foo').

Поскольку module предоставляет свойство filename (обычно эквивалентное __filename), точку входа текущего приложения можно получить, проверив require.main.filename.

Дополнительные сведения: Советы по менеджерам пакетов

Семантика функции 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.

Для того чтобы сделать модули доступными в Node.js REPL, может быть полезно добавить папку /usr/lib/node_modules в переменную окружения $NODE_PATH. Поскольку поиск модулей с использованием папок node_modules является относительным и основан на фактическом пути файлов, вызывающих require(), сами пакеты могут находиться где угодно.

Всё вместе…

Чтобы получить точное имя файла, который будет загружен при вызове require(), используйте функцию require.resolve().

Собирая всё вышесказанное, вот алгоритм высокого уровня в псевдокоде того, что делает require.resolve():

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 be the filesystem root
3. If X begins with './' or '/' or '../'
   a. LOAD_AS_FILE(Y + X)
   b. LOAD_AS_DIRECTORY(Y + X)
4. LOAD_NODE_MODULES(X, dirname(Y))
5. THROW "not found"

LOAD_AS_FILE(X)
1. If X is a file, load X as JavaScript text.  STOP
2. If X.js is a file, load X.js as JavaScript text.  STOP
3. If X.json is a file, parse 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, load X/index.js as JavaScript text.  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. let M = X + (json main field)
   c. LOAD_AS_FILE(M)
   d. LOAD_INDEX(M)
2. LOAD_INDEX(X)

LOAD_NODE_MODULES(X, START)
1. let DIRS=NODE_MODULES_PATHS(START)
2. for each DIR in DIRS:
   a. LOAD_AS_FILE(DIR/X)
   b. 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" CONTINUE
   b. DIR = path join(PARTS[0 .. I] + "node_modules")
   c. DIRS = DIRS + DIR
   d. let I = I - 1
5. return DIRS

Кэширование

Модули кэшируются после первой загрузки. Это означает (среди прочего), что каждый вызов require('foo') вернёт ровно тот же объект, если он будет разрешен в тот же файл.

Многократные вызовы require('foo') могут не вызывать многократного выполнения кода модуля. Это важная функция. С её помощью можно возвращать "частично готовые" объекты, позволяя загружать транзитивные зависимости даже при возникновении циклов.

Чтобы модуль выполнял код несколько раз, экспортируйте функцию и вызывайте её.

Ограничения кэширования модулей

Модули кэшируются на основе разрешённого имени файла. Поскольку модули могут разрешаться в разные имена файлов в зависимости от местоположения вызывающего модуля (загрузка из папок node_modules), нет гарантии, что require('foo') всегда вернёт точно такой же объект, если он разрешится в разные файлы.

Кроме того, в системах файлов или операционных системах, не чувствительных к регистру, разные разрешённые имена файлов могут указывать на один и тот же файл, но кэш всё равно будет рассматривать их как разные модули и перезагружать файл несколько раз. Например, require('./foo') и require('./FOO') возвращают два разных объекта, независимо от того, являются ли ./foo и ./FOO одним и тем же файлом.

Ядерные модули

Node.js имеет несколько модулей, скомпилированных в двоичный файл. Эти модули подробно описаны в других разделах этой документации.

Ядерные модули определены в исходном коде Node.js и находятся в папке lib/.

Ядерные модули всегда имеют преимущество при загрузке, если их идентификатор передаётся в require(). Например, require('http') всегда вернёт встроенный модуль HTTP, даже если существует файл с таким именем.

Циклы

При наличии циклических вызовов 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.

К тому времени, когда 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

Для корректной работы циклических зависимостей модулей требуется тщательное планирование.

Модули файлов

Если точное имя файла не найдено, Node.js попытается загрузить необходимый файл с добавленными расширениями: .js, .json, и, наконец, .node.

Файлы .js интерпретируются как текстовые файлы JavaScript, а файлы .json парсятся как текстовые файлы JSON. Файлы .node интерпретируются как скомпилированные модули дополнений, загружаемые с помощью dlopen.

Модуль, начинающийся с '/', представляет собой абсолютный путь к файлу. Например, require('/home/marco/foo.js') загрузит файл по пути /home/marco/foo.js.

Модуль, начинающийся с './', является относительным по отношению к файлу, вызывающему require() . То есть circle.js должен находиться в той же директории, что и foo.js для того, чтобы require('./circle') его нашёл.

Без ведущего '/', './' или '../' для указания файла, модуль должен быть либо ядерным модулем, либо загружен из папки node_modules.

Если указанный путь не существует, require() вызовет Error с его свойством code установленным в значение 'MODULE_NOT_FOUND'.

Папки как модули

Удобно организовывать программы и библиотеки в самостоятельные каталоги и предоставлять единую точку входа в эту библиотеку. Есть три способа передачи папки в require() в качестве аргумента.

Первый способ заключается в создании файла package.json в корне папки, который указывает на модуль main. Пример файла package.json может выглядеть так:

{ "name" : "some-library",
  "main" : "./lib/some-library.js" }

Если этот файл находится в папке по пути ./some-library, тогда require('./some-library') попытается загрузить ./some-library/lib/some-library.js.

Это всё, что Node.js знает о файлах package.json.

Примечание: Если файл, указанный в поле 'main' файла package.json отсутствует и не может быть разрешён, Node.js сообщит об отсутствии всего модуля с использованием стандартной ошибки:

Error: Cannot find module 'some-library'

Если в директории нет файла package.json, Node.js попытается загрузить файл index.js или index.node из этой директории. Например, если в вышеприведённом примере не было файла package.json, то require('./some-library') попытался бы загрузить:

  • ./some-library/index.js
  • ./some-library/index.node

Загрузка из папок 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_prefix Node.js.

Это в основном по историческим причинам.

Примечание: Настоятельно рекомендуется размещать зависимости в локальной папке node_modules. Это обеспечит более быструю и надёжную загрузку.

Обёртка модуля

Перед выполнением кода модуля Node.js обернёт его функцией-обёрткой, которая выглядит следующим образом:

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

Благодаря этому Node.js достигает нескольких целей:

  • Он сохраняет переменные верхнего уровня (определённые с помощью var, const или let в области видимости модуля, а не глобального объекта.
  • Он помогает предоставить некоторые глобальные переменные, которые на самом деле являются специфичными для модуля, например:
    • Объекты module и exports, которые разработчик может использовать для экспорта значений из модуля.
    • Удобные переменные __filename и __dirname, содержащие абсолютный путь к файлу и каталогу модуля.

Область видимости модуля

__dirname

Добавлен в: v0.1.27
  • <строка>

Имя каталога текущего модуля. Это то же самое, что path.dirname() для __filename.

Пример: запуск node example.js из /Users/mjr

console.log(__dirname);
// Prints: /Users/mjr
console.log(path.dirname(__filename));
// Prints: /Users/mjr

__filename

Добавлен в: v0.0.1
  • <строка>

Имя файла текущего модуля. Это разрешённый абсолютный путь к файлу текущего модуля.

Для основной программы это не обязательно совпадает с именем файла, используемым в командной строке.

См. __dirname для имени каталога текущего модуля.

Примеры:

Запуск node example.js из /Users/mjr

console.log(__filename);
// Prints: /Users/mjr/example.js
console.log(__dirname);
// Prints: /Users/mjr

Рассмотрим два модуля: 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

Добавлен в: v0.1.12

Ссылка на module.exports, более короткая для набора. Подробности о том, когда использовать exports и когда module.exports, см. в разделе об сокращении exports.

module

Добавлен в: v0.1.16
  • <Объект>

Ссылка на текущий модуль, см. раздел о module объекте. В частности, module.exports используется для определения того, что экспортирует модуль, и что доступно через require().

require()

Добавлен в: v0.1.13
  • <Функция>

Используется для импорта модулей, JSON, и локальных файлов. Модули могут быть импортированы из node_modules. Локальные модули и JSON-файлы могут быть импортированы с использованием относительного пути (например, ./, ./foo, ./bar/baz, ../foo), который будет разрешён относительно каталога, указанного в __dirname (если определён) или текущем рабочем каталоге.

// Importing a local module:
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('crypto');

require.cache

Добавлен в: v0.3.0
  • <Объект>

Модули кэшируются в этом объекте при их требовании. Удаление значения ключа из этого объекта приведет к повторной загрузке модуля при следующем require. Обратите внимание, что это не относится к нативным дополнениям, для которых повторная загрузка приведет к ошибке.

require.extensions

Добавлен в: v0.3.0Устарел начиная с: v0.10.6
Уровень стабильности: 0 - Устарел
  • <Объект>

Указывает Node.js, как обрабатывать определённые расширения файлов.

Обработка файлов с расширением .sjs как .js:

require.extensions['.sjs'] = require.extensions['.js'];

Устарел В прошлом этот список использовался для загрузки в Node.js модулей, отличных от JavaScript, компилируя их по требованию. Однако на практике существуют лучшие способы сделать это, такие как загрузка модулей через другую программу Node.js или компиляция их в JavaScript заранее.

Поскольку система модулей заблокирована, эта функция, вероятно, не будет удалена. Однако в ней могут быть скрытые ошибки и сложности, которые лучше оставить нетронутыми.

Обратите внимание, что количество операций с файловой системой, которые должна выполнить система модулей для разрешения оператора require(...) в имя файла, увеличивается линейно с количеством зарегистрированных расширений.

Другими словами, добавление расширений замедляет загрузчик модулей и следует избегать.

require.resolve(request[, options])

История
Версия Изменения
v8.9.0

Теперь поддерживается опция paths.

v0.3.0

Добавлен в: v0.3.0

  • request <строка> Путь к модулю для разрешения.
  • options <объект>
    • paths <массив> Пути для разрешения расположения модуля. Если присутствуют, эти пути используются вместо стандартных путей разрешения. Обратите внимание, что каждый из этих путей используется в качестве отправной точки для алгоритма разрешения модулей, что означает, что иерархия node_modules проверяется с этого расположения.
  • Возвращает: <строка>

Использует внутренние механизмы require() для поиска расположения модуля, но вместо загрузки модуля просто возвращает разрешённое имя файла.

require.resolve.paths(request)

Добавлен в: v8.9.0
  • request <строка> Путь к модулю, чьи пути поиска нужно получить.
  • Возвращает: <массив> | <null>

Возвращает массив путей, просмотренных во время разрешения request, или null, если строка request ссылается на модуль ядра, например http или fs.

Объект module

Добавлен в: v0.1.16
  • <Объект>

В каждом модуле переменная module — ссылка на объект, представляющий текущий модуль. Для удобства переменная module.exports также доступна через глобальную переменную модуля exports. Переменная module на самом деле не глобальная, а локальная для каждого модуля.

module.children

Добавлена в: v0.1.16
  • <Массив>

Объекты модулей, необходимые этому.

module.exports

Добавлена в: v0.1.16
  • <Объект>

Объект module.exports, созданный системой модулей. Иногда это не подходит; многие хотят, чтобы их модуль был экземпляром некоторого класса. Для этого назначьте желаемый экспортный объект module.exports. Обратите внимание, что назначение желаемого объекта exports просто переопределит локальную переменную exports, что, вероятно, не является желаемым действием.

Например, предположим, что мы создаем модуль под названием a.js

const EventEmitter = require('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);

Затем в другом файле мы можем сделать следующее

const a = require('./a');
a.on('ready', () => {
  console.log('module a is ready');
});

Обратите внимание, что присваивание module.exports должно выполняться немедленно. Оно не может выполняться в каких-либо коллбеках. Это не работает:

x.js:

setTimeout(() => {
  module.exports = { a: 'hello' };
}, 0);

y.js:

const x = require('./x');
console.log(x.a);

Сокращение exports

Добавлена в: v0.1.16

Переменная 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

Когда свойство module.exports полностью заменяется новым объектом, обычно также переопределяется exports, например:

module.exports = exports = function Constructor() {
  // ... etc.
};

Чтобы проиллюстрировать поведение, представьте себе эту гипотетическую реализацию 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;
}

module.filename

Добавлена в: v0.1.16
  • <строка>

Полное разрешенное имя файла модуля.

module.id

Добавлена в: v0.1.16
  • <строка>

Идентификатор модуля. Обычно это полное разрешенное имя файла.

module.loaded

Добавлена в: v0.1.16
  • <логическое значение>

Загружен ли модуль или находится в процессе загрузки.

module.parent

Добавлена в: v0.1.16
  • <Объект> Объект модуля

Модуль, который сначала потребовал этот.

module.paths

Добавлена в: v0.4.0
  • <массив строк>

Пути поиска модуля.

module.require(id)

Добавлена в: v0.5.1
  • id <строка>
  • Возвращает: <Объект> module.exports из разрешенного модуля

Метод module.require предоставляет способ загрузки модуля так, как будто require() был вызван из исходного модуля.

Примечание: Для этого необходимо получить ссылку на объект module. Так как require() возвращает module.exports, и объект module обычно только доступен в коде конкретного модуля, его необходимо явно экспортировать, чтобы использовать его.

Объект Module

Добавлена в: v0.3.7
  • <Объект>

Предоставляет общие служебные методы при взаимодействии с экземплярами Module — переменной module , часто встречающейся в модулях файлов. Доступ к нему осуществляется через require('module').

module.builtinModules

Добавлена в: v8.10.0
  • <массив строк>

Список имен всех модулей, предоставляемых Node.js. Может использоваться для проверки, поддерживается ли модуль сторонним модулем или нет.

© 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/dist/latest-v8.x/docs/api/modules.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API