Чем отличаются import и require в Node.js и как их совмещать?
Два разных модуля, две разных эпохи
Node.js исторически поддерживал собственную систему модулей — CommonJS (require/module.exports), а затем добавил поддержку стандарта ES Modules (import/export), который используется в браузерах и современном JS. Это не два синтаксиса одного и того же — это две разные системы модулей со своими правилами загрузки, разрешения путей и жизненного цикла.
CommonJS (require)
- Загрузка синхронная: файл читается, выполняется и кэшируется в момент вызова
require. require— это обычная функция, поэтому её можно вызывать условно, в цикле, внутри функции.- Экспорт через
module.exportsилиexports.xxx. - Внутри модуля доступны
__dirname,__filename,require,module— они инжектятся Node автоматически. - Расширение по умолчанию для CommonJS —
.js(если вpackage.jsonнет"type": "module") или.cjs.
// math.js (CommonJS)
function sum(a, b) {
return a + b;
}
module.exports = { sum };
// index.js
const { sum } = require('./math');
console.log(sum(2, 3));
ES Modules (import)
- Загрузка асинхронная, разрешение импортов статически анализируется до выполнения кода (hoisting).
importнельзя вызвать условно или внутри блока — только на верхнем уровне файла (для динамики естьimport()).- Экспорт через
export/export default. - Нет
__dirname,__filename,require"из коробки" — их получают черезimport.meta.url. - Поддерживает top-level await.
- Включается через
"type": "module"вpackage.jsonили расширение.mjs.
// math.mjs (ES Module)
export function sum(a, b) {
return a + b;
}
// index.mjs
import { sum } from './math.mjs';
console.log(sum(2, 3));
Как совмещать
- Явно указывать формат расширением:
.cjs— всегда CommonJS,.mjs— всегда ESM, независимо от"type"вpackage.json. - Импорт CJS из ESM работает "из коробки":
import pkg from './cjs-module.cjs'— весьmodule.exportsпридёт как default-экспорт. - Импорт ESM из CJS напрямую через
requireне работает (ESM асинхронен) — нужно использовать динамическийimport(), который возвращает Promise. - Для доступа к
requireвнутри ESM-файла используютcreateRequireиз модуляmodule. - В
package.jsonможно задать"exports"с полями"require"и"import", чтобы один пакет отдавал разные точки входа в зависимости от того, кто его подключает.
// внутри ESM: получить require и подключить CJS-модуль
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const legacy = require('./legacy-cjs-module.js');
// внутри CJS: подключить ESM-модуль
async function loadEsm() {
const mod = await import('./esm-module.mjs');
mod.doSomething();
}
На практике в новых проектах предпочитают ESM как стандарт, а совместимость с CJS поддерживают через exports-мапу в package.json и точечные createRequire/import() там, где нужно смешивать системы.
Что хочет услышать интервьюер
Понимание, что CommonJS и ESM — это две разные системы модулей, а не просто разный синтаксис
Знание ключевых синтаксических отличий: module.exports/require vs export/import
Понимание синхронной природы require и асинхронной, статической природы import
Знание способов включить ESM в проекте: "type": "module", расширения .mjs/.cjs
Знание практических приёмов совмещения: createRequire, динамический import(), поле exports в package.json
Пример: CommonJS: экспорт и импорт
// math.js (CommonJS)
function sum(a, b) {
return a + b;
}
module.exports = { sum };
// index.js
const { sum } = require('./math');
console.log(sum(2, 3));
Пример: ES Modules: экспорт и импорт
// math.mjs (ES Module)
export function sum(a, b) {
return a + b;
}
// index.mjs
import { sum } from './math.mjs';
console.log(sum(2, 3));
Пример: Подключить CommonJS-модуль из ESM через createRequire
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const legacy = require('./legacy-cjs-module.js');
Пример: Подключить ESM-модуль из CommonJS через динамический import()
async function loadEsm() {
const mod = await import('./esm-module.mjs');
mod.doSomething();
}
loadEsm();
Типичные ошибки
Считают import и require полностью взаимозаменяемыми, не зная об ограничениях статического import
Пытаются использовать import условно или внутри блока, забывая про import()
Не знают, что в ESM отсутствуют __dirname, __filename и require по умолчанию
Забывают указать "type": "module" или расширение .mjs, из-за чего import падает с ошибкой
Пытаются require ESM-модуль напрямую, не зная, что нужен динамический import()


