В этом руководстве вы узнаете о модулях Node.js и CommonJS и поймете, как они работают.
Node.js по умолчанию поддерживает модульную систему, называемую модулями CommonJS. Начиная с версии 14.0.0 он поддерживает модули ES. В этом руководстве основное внимание уделяется модулям CommonJS.
В модулях рассматривает каждый файл JavaScript как отдельный модуль.
Давайте разберем простой пример, чтобы увидеть, как модули работают в Node.js.
Создание нового модуля
Сначала создайте новый файл с именем logger.js
со следующим кодом:
const error = 'ERROR'; const warning = 'WARNING'; const info = 'INFO'; function log(message, level = info) { console.log(`${level}: ${message}`); }
logger.js
содержит три константы и функцию.
Функция log()
в модуле logger
принимает два параметра: message
и level
. Если вы не передаете level
в функцию log()
, по умолчанию используется info
.
В модуле logger.js
все переменные и функции являются приватными. Это означает, что они невидимы и не могут использоваться в других модулях.
Чтобы использовать переменные и функции модуля в другом модуле, вам необходимо экспортировать их в конец файла logger.js
:
module.exports.log = log; module.exports.error = error; module.exports.info = info; module.exports.warning = warning;
Этот код создает новые свойства объекта module.exports
и назначает их соответствующим переменным и функциям.
Также вы можете использовать разные имена при экспорте объектов. Например:
module.exports.fatal = error;
В этом случае другие модули будут ссылаться на константу error
как на fatal
константу.
Теперь вы готовы использовать функцию log()
и все константы модуля logger
в другом модуле.
Импорт
Во-вторых, создайте новый файл с именем app.js
, который использует модуль logger.js
. Чтобы использовать модуль logger.js
из app.js
, вам необходимо импортировать модуль logger
с помощью функции require()
:
const logger = require('./logger.js');
Или вы можете удалить расширение .js
из logger.js
следующим образом:
const logger = require('./logger');
Незаметно функция require()
выполняет файл logger.js
и возвращает объект exports
. Если функция require()
не может найти файл, она выдаст ошибку.
Ниже показаны объекты logger
для консоли:
const logger = require('./logger'); console.log(logger);
Выход:
{ log: [Function: log], error: 'ERROR', info: 'INFO', warning: 'WARNING' }
Объект logger
содержит функцию log()
и другие константы из модуля logger.js
. Вы можете ссылаться на них следующим образом:
const logger = require('./logger'); logger.log('Node.js module demo 1'); logger.log('Node.js module demo 2', logger.warning);
Выход:
INFO: Node.js module demo 1 WARNING: Node.js module demo 2
Чтобы сделать код более лаконичным, вы можете использовать деструктуризацию объекта при импорте модуля следующим образом:
const { log, error, info, warning } = require('./logger');
Деструктуризация объекта присваивает свойства объекта exports
, возвращаемого функцией require()
, переменным с левой стороны.
После деструктуризации вы можете напрямую использовать эти переменные:
log('Node.js module demo 1'); log('Node.js module demo 2', warning);
Выход:
INFO: Node.js module demo 1 WARNING: Node.js module demo 2
Понимание модуля функции-оболочки
Прежде чем Node.js выполнит модуль, он оборачивает весь код внутри этого модуля с помощью функции-оболочки, которая выглядит следующим образом:
(function(exports, require, module, __filename, __dirname) { // Module code });
Например, код модуля logger.js
перед выполнением будет выглядеть так:
(function(exports, require, module, __filename, __dirname) { const error = 'ERROR'; const warning = 'WARNING'; const info = 'INFO'; function log(message, level = info) { console.log(`${level}: ${message}`); } module.exports.log = log; module.exports.error = error; module.exports.info = info; module.exports.warning = warning; });
Делая это, Node.js достигает следующих важных целей:
- Удержание переменных верхнего уровня (
var
,let
иconst
) привязанными к модулю, а не к глобальному объекту. - Создание некоторых специфичных для модуля переменных, таких как глобальные переменные, например
module
иexports
.
Обратите внимание, что объект exports
ссылается на module.exports
:
console.log(module.exports === exports); // true
Импорт одного и того же модуля несколько раз
Когда вы используете функцию require()
для включения модуля несколько раз, функция require()
оценивает модуль только один раз при первом вызове и помещает его в кеш.
Из последующих вызовов функция require()
использует объект экспорта из кеша вместо повторного выполнения модуля.
Следующий пример иллюстрирует, как это работает:
Сначала создайте новый модуль с именем dblogger.js
со следующим кодом:
console.log('Connected to the DB');
Затем используйте функцию require()
, чтобы несколько раз включить модуль dblogger.js
в app.js
:
let dbLogger = require('./dblogger'); dbLogger = require('./dblogger');
Выход:
DBLogger is loaded.
В этом примере вы можете увидеть сообщение 'DBLogger is loaded.'
только один раз, а не два. Это означает, что Node.js оценивал dblogger.js
только один раз.
Заключение
- В модулях CommonJS Node.js рассматривает файл JavaScript как модуль.
- Предоставьте доступ к переменным и функциям другому модулю, назначив их свойствам объекта
module.exports
. - Node.js оборачивает код модуля в функцию-оболочку модуля перед его выполнением.
- Все переменные, константы, функции, классы и т. д., объявленные в модуле, относятся к модулю, а не к глобальной области.
- Node.js выполняет модуль только один раз и помещает результат в кеш для следующего использования.