Перейти к содержанию
Innovialabs

ES Modules vs CommonJS: import и require в JavaScript

Основы3 мин

Коротко

CommonJS (require) — старая синхронная модульная система Node.js, ES Modules (import) — официальный стандарт языка; Node.js выбирает систему по расширению файла и полю type в package.json.

Коротко о разнице

В JavaScript есть две модульные системы:

  • CommonJS (CJS) — require() и module.exports. Появилась в Node.js задолго до стандарта и до сих пор встречается в старых пакетах и конфигах.
  • ES Modules (ESM) — import и export. Официальный стандарт языка, работает и в браузерах, и в Node.js.

Для нового кода выбирайте ESM. CommonJS нужно понимать, чтобы читать существующий код и разбираться с ошибками совместимости.

// CommonJS
const fs = require('fs');
function sum(a, b) { return a + b; }
module.exports = { sum };

// ES Modules
import fs from 'node:fs';
export function sum(a, b) { return a + b; }

Чем они отличаются по сути

CommonJSES Modules
Синтаксисrequire, module.exportsimport, export
Загрузкасинхронная, во время выполнениястатический анализ до выполнения
Условный импортrequire где угодноimport() — динамический, возвращает Promise
Top-level awaitнетесть
__dirname, __filenameестьнет, используется import.meta
Строгий режимпо желаниювсегда включён
Tree shakingплохо работаетработает

Главное практическое следствие: ESM-импорты статичны. Сборщик заранее видит, что и откуда импортируется, и может выбросить неиспользуемый код. С require это сделать сложнее, потому что путь может вычисляться во время работы.

Как Node.js решает, какая система у файла

Node.js смотрит на расширение и package.json:

  • .mjs — всегда ESM.
  • .cjs — всегда CommonJS.
  • .js — зависит от поля type в ближайшем package.json: "module" означает ESM, "commonjs" или отсутствие поля — CommonJS.

Чтобы перевести проект на ESM:

{
  "type": "module"
}

После этого учтите:

  • В относительных импортах нужно указывать расширение: import { sum } from './math.js'.
  • __dirname больше нет. Используйте import.meta.url вместе с fileURLToPath или import.meta.dirname в свежих версиях Node.js.
  • Конфиги инструментов, которые ещё ожидают CommonJS, переименуйте в .cjs.

Откуда берутся типичные ошибки

«Cannot use import statement outside a module» — файл с import выполняется как CommonJS. Причины:

  • в package.json нет "type": "module", а файл имеет расширение .js;
  • в браузере скрипт подключён без type="module";
  • тестовый раннер или старый инструмент не настроен на ESM.

Решение: добавить "type": "module", переименовать файл в .mjs или подключить <script type="module">.

«require is not defined in ES module scope» — обратная ситуация: в ESM-файле вызывают require. Замените на import или используйте createRequire из модуля node:module.

ERR_REQUIRE_ESM — CommonJS-код пытается через require загрузить пакет, который распространяется только как ESM. Новые версии Node.js умеют require() для ES-модулей без top-level await, в старых поможет только динамический import() или переход на ESM.

«Cannot find module './utils'» в ESM — забыли расширение файла в пути.

Правила совместного использования

ESM импортирует CommonJS — работает почти всегда:

import pkg from './legacy.cjs';      // module.exports целиком
import { helper } from './legacy.cjs'; // работает, если Node.js смог определить экспорты статически

Если именованный импорт не находится, импортируйте объект по умолчанию и деструктурируйте его.

CommonJS загружает ESM — сложнее:

// в CommonJS-файле
async function main() {
  const { sum } = await import('./math.mjs');
  console.log(sum(2, 3));
}
main();

Динамический import() работает в CommonJS всегда, но он асинхронный.

Как выбрать для проекта

  • Новое приложение — ESM, "type": "module".
  • Next.js, Vite и другие современные фреймворки — пишите import/export, сборщик разберётся сам.
  • Старый Node.js-сервис на CommonJS — переводите постепенно: новые файлы как .mjs или переход целиком, когда есть тесты.
  • Библиотека для npm — ESM как основной формат; если нужны старые потребители, публикуйте и CommonJS-сборку через поле exports.

Частые вопросы

Браузер исполняет файл как модуль, если он подключён через <script type="module">. Node.js по умолчанию считает .js файлы CommonJS, пока вы не укажете "type": "module" или расширение .mjs.

Поможем с этим

Если хотите не разбираться самим, а получить результат — этим мы занимаемся каждый день.

Обсудить проект

Читайте также

Есть идея или задача? Расскажите нам

Ответим в течение рабочего дня и предложим решение. Консультация бесплатная.