Перейти к содержимому
17 / 33

В чём разница между CommonJS и ES Modules?


Полный ответ

Две системы делают одно и то же — экспорт и импорт кода, — но работают по-разному:

JavaScript
// === CommonJS ===
// math.js

function add(a, b) {
  return a + b;
}
module.exports = { add };

// app.js
const { add } = require("./math");
console.log(add(2, 3)); // 5

JavaScript
// === ES Modules ===
// math.js

export function add(a, b) {
  return a + b;
}

// app.js
import { add } from "./math.js";
console.log(add(2, 3)); // 5

Ключевые различия:

JavaScript
// 1. СИНХРОННОСТЬ vs АСИНХРОННОСТЬ

// CJS — синхронный, блокирует выполнение
const fs = require("fs"); // код ждёт, пока модуль загрузится

// ESM — асинхронный, парсит зависимости заранее
import fs from "node:fs"; // зависимости известны до выполнения

// 2. ДИНАМИЧЕСКИЙ vs СТАТИЧЕСКИЙ require

// CJS — можно вызвать require() в любом месте
if (needsDatabase) {
  const db = require("./db"); // загрузится только если условие true
}

// ESM — import только на верхнем уровне (для динамики есть import())
import { db } from "./db.js"; // всегда на верхнем уровне
// динамический вариант:
if (needsDatabase) {
  const { db } = await import("./db.js");
}

// 3. ЭКСПОРТ — КОПИЯ vs ЖИВАЯ ССЫЛКА

// CJS — экспортирует копию значения
// counter.js
let count = 0;
module.exports = { count, increment: () => ++count };

// app.js (CJS)
const counter = require("./counter");
counter.increment();
console.log(counter.count); // 0 — копия не обновилась!

// ESM — экспортирует живую привязку (live binding)
// counter.js
export let count = 0;

export function increment() { count++; }

// app.js (ESM)
import { count, increment } from "./counter.js";
increment();
console.log(count); // 1 — живая ссылка обновилась

Как включить ESM в Node.js:

JSON
// package.json — способ 1: для всего проекта
{
  "type": "module"
}

// Способ 2: расширение файла
// .mjs — всегда ESM
// .cjs — всегда CJS

Реальные кейсы

Совместимость — библиотека, которая поддерживает оба формата:

JSON
// package.json
{
  "main": "./dist/index.cjs",
  "module": "./dist/index.mjs",
  "exports": {
    ".": {
      "import": "./dist/index.mjs",
      "require": "./dist/index.cjs"
    }
  }
}

Ошибки при смешивании:

JavaScript
// ❌ require() в ESM-файле — не работает
import { readFile } from "node:fs";
const lodash = require("lodash"); // ReferenceError: require is not defined

// ✅ Используй import или createRequire
import { createRequire } from "node:module";
const require = createRequire(import.meta.url);
const lodash = require("lodash"); // теперь работает

Резюме

CJS — наследие Node.js, синхронный, гибкий, но не оптимизируемый. ESM — стандарт языка, статический, с живыми привязками и поддержкой tree shaking. Новые проекты стоит писать на ESM, а CJS оставить для обратной совместимости.

В чём разница между CommonJS и ES Modules? | JScriptiser