Модули Node.js и CommonJS в JavaScript

В этом руководстве вы узнаете о модулях Node.js и CommonJS и поймете, как они работают.

Node.js по умолчанию поддерживает модульную систему, называемую модулями CommonJS. Начиная с версии 14.0.0 он поддерживает модули ES. В этом руководстве основное внимание уделяется модулям CommonJS.

В модулях рассматривает каждый файл JavaScript как отдельный модуль.

Давайте разберем простой пример, чтобы увидеть, как модули работают в Node.js.

Как работают модули в 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 выполняет модуль только один раз и помещает результат в кеш для следующего использования.
Рейтинг
( Пока оценок нет )
Александр Русаков / автор статьи
Программист, разработчик, 12 лет опыта работы в крупных компаниях. Быстро освоил typescript, делюсь своими знаниями на страницах этого сайта.
Загрузка ...
JavaScript и TypeScript