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 оставить для обратной совместимости.