Чем отличаются import и require в Node.js и как их совмещать?

JuniorNode.js · Backend·Обновлено 5 сентября 2026
Коротко
require — это синхронная загрузка модулей CommonJS с module.exports, а import — статический, асинхронный синтаксис ES Modules с поддержкой top-level await; совмещать их можно через расширения .cjs/.mjs, поле "type" в package.json, createRequire и динамический import().

Два разных модуля, две разных эпохи

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));

Как совмещать

  1. Явно указывать формат расширением: .cjs — всегда CommonJS, .mjs — всегда ESM, независимо от "type" в package.json.
  2. Импорт CJS из ESM работает "из коробки": import pkg from './cjs-module.cjs' — весь module.exports придёт как default-экспорт.
  3. Импорт ESM из CJS напрямую через require не работает (ESM асинхронен) — нужно использовать динамический import(), который возвращает Promise.
  4. Для доступа к require внутри ESM-файла используют createRequire из модуля module.
  5. В 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()

Лучшие курсы по теме

изображение курса

Docker и Ansible

Антон Ларичев
AI-тренажерыAI-тренажеры
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Node.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Nest.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.6
3 999 ₽ 6 990 ₽
Подробнее