CommonJS: require и module.exports
Как Node делит программу на файлы-модули и связывает их через require и module.exports.
Модуль — это отдельный файл с кодом. В системе CommonJS файл экспортирует то, что хочет отдать наружу, через
module.exports, а подключают его черезrequire.
Зачем вообще модули
Держать всю программу в одном файле невозможно: код становится нечитаемым, переменные конфликтуют, переиспользовать ничего нельзя. Модули разбивают программу на части, каждая со своей зоной ответственности. Node исторически использует систему CommonJS.
Экспорт: module.exports
Создадим файл math.js, который отдаёт наружу две функции. То, что мы присвоим в module.exports, станет доступно другим файлам:
// math.js
function add(a, b) {
return a + b;
}
function multiply(a, b) {
return a * b;
}
module.exports = { add, multiply };
Всё, что НЕ попало в module.exports, остаётся приватным внутри файла — снаружи к нему не подобраться. Это инкапсуляция «из коробки».
Импорт: require
Теперь подключим этот модуль в другом файле. Функция require возвращает то, что модуль экспортировал. Путь к своему файлу указывают с ./:
// app.js
const math = require("./math");
console.log(math.add(2, 3)); // 5
console.log(math.multiply(4, 5)); // 20
// или сразу разобрать на части:
const { add } = require("./math");
console.log(add(10, 1)); // 11
Запустим node app.js — Node найдёт math.js, выполнит его, заберёт module.exports и подставит в переменную.
Несколько способов экспортировать
Можно собирать экспорт по частям через exports (это короткая ссылка на module.exports):
// logger.js
exports.info = (msg) => console.log("[INFO]", msg);
exports.warn = (msg) => console.log("[WARN]", msg);
А можно экспортировать один-единственный объект или функцию, присвоив его прямо в module.exports:
// config.js
module.exports = {
port: 3000,
host: "localhost"
};
Важно: переприсваивать нужно именно module.exports. Если написать exports = {...}, связь потеряется, и наружу ничего не уйдёт.
Модуль выполняется один раз
Когда вы require один и тот же модуль несколько раз, его код выполняется только при первом подключении, а результат кешируется. Это удобно: общий объект-конфиг или подключение к БД создаётся единожды.
Сама идея «собрать публичный интерфейс в объект» — это чистый JavaScript. Вот аналог без файлов, который реально исполнится:
function createMathModule() {
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;
return { add, multiply }; // публичный интерфейс
}
const math = createMathModule();
console.log(math.add(2, 3));
console.log(math.multiply(4, 5));
Вывод:
5 20
Итог
- Каждый файл в Node — отдельный модуль со своей областью видимости.
- Экспорт наружу — через
module.exports(илиexports.имя). - Импорт — через
require("./путь"); для своих файлов путь с./. - Код модуля выполняется один раз и кешируется.