Skip to main content

ESM проти CommonJS

1. Коротко: головна ідея

СистемаОсновна концепція
CommonJS (CJS)Модулі завантажуються під час виконання (runtime), синхронно, порядково.
ES Modules (ESM)Модулі аналізуються на етапі компіляції (parse-time), завантажуються асинхронно, із суворими залежностями та «живими» імпортами.

2. Філософія систем

CommonJS

Створювався для Node.js (2009 рік), щоб виконувати JS на сервері.

  • Усе синхронно (адже файли - на локальному диску);
  • Кожен файл - ізольований модуль;
  • Код виконується згори вниз під час першого require();
  • Імпорти - це копія значень, а не «живі посилання».

Приклад:

javascript
// math.js module.exports = { x: 1 }; // app.js const math = require('./math'); console.log(math.x); // 1 math.x = 2; // зміна лише локальної копії

ES Modules

Створювалися для мови JavaScript (ES6, 2015 рік), щоб один стандарт працював і в браузерах, і в Node.js.

  • Завантаження асинхронне, декларативне;
  • Імпорти статичні - відомі ще до виконання коду;
  • Можна оптимізувати, робити tree-shaking;
  • Імпорти - це live bindings, тобто «живі» посилання, а не копії.

Приклад:

javascript
// counter.js export let count = 0; export function inc() { count++; } // app.js import { count, inc } from './counter.js'; inc(); console.log(count); // 1 (значення оновилося)

3. Концептуальні відмінності

КонцепціяCommonJSES Modules
Час завантаженняПід час виконання (runtime)До виконання (parse-time)
ІмпортиДинамічні (require())Статичні (import)
Тип зв'язкуЗначення копіюютьсяЖиві посилання (live bindings)
Порядок виконанняЗгори внизВизначається залежностями
АсинхронністьСинхронноАсинхронно
ОптимізаціяНеможлива (runtime)Можливий tree-shaking
Контекстthis = module.exportsthis = undefined
КешуванняПісля першого викликуЄ, але із «живими» значеннями
ЕкосистемаNode.js, старі пакетиСучасний JS, браузери, Node 13+
АрхітектураІмперативнаДекларативна
СумісністьЛише серверУніверсальна (сервер + клієнт)

4. Різниця в моменті завантаження і компіляції

CommonJS:

  • Виконується рядок require() → читається файл → виконується код.

  • Тому шлях може бути змінною:

    javascript
    const lib = require(condition ? './a' : './b');

Працює, тому що це звичайна функція.

ES Modules:

  • Імпорти повинні бути у верхній частині файлу і не можуть бути динамічними:

    javascript
    import x from './a'; // коректно if (cond) import x from './b'; // помилка
  • Node.js і браузер знають заздалегідь, які залежності потрібні,
    і можуть завантажувати їх паралельно до виконання коду.

5. Різниця в структурі модуля

Node.js «обгортає» CommonJS-модуль у внутрішню функцію:

javascript
(function (exports, require, module, __filename, __dirname) { // твій код });

Тому є:

  • require
  • module.exports
  • __dirname
  • __filename

У ESM цього немає:

  • замість __dirnameimport.meta.url
  • require недоступний

6. Поведінка під час кешування

У CommonJS модуль виконується один раз і кешується:

javascript
const a = require('./a'); const b = require('./a'); console.log(a === b); // true

У ESM теж є кеш, але імпорти залишаються живими (якщо змінні експортовані як let/const):

javascript
import { counter } from './a.js';

Якщо модуль оновлює значення, усі імпорти бачать оновлення.

7. Tree-shaking та оптимізація

Tree-shaking - це видалення невикористаного коду (у Webpack, Rollup, Vite тощо).

  • CJS: неможливо, тому що імпорти динамічні;
  • ESM: можливо, тому що залежності статичні.

Приклад:

javascript
import { used } from './utils.js'; // бандлер знає, що потрібен лише used

8. Сумісність

СценарійCommonJSES Modules
Використовувати старі пакетиТакІноді потрібен createRequire()
SSR / серверВідмінноВідмінно (Node 13+)
БраузериНіТак
Tree-shakingНіТак
Top-level awaitНіТак

9. «Живі» проти «мертвих» імпортів (приклад)

CommonJS:

javascript
// counter.js let count = 0; module.exports = { count, inc() { count++; } }; // app.js const c = require('./counter'); c.inc(); console.log(c.count); // 0 (копія)

ES Modules:

javascript
// counter.mjs export let count = 0; export function inc() { count++; } // app.mjs import { count, inc } from './counter.mjs'; inc(); console.log(count); // 1 (живе посилання)

10. Концептуально: ключові відмінності

ПараметрCommonJSESM
ОсноваNode.js (сервер)Стандарт ECMAScript
Модель виконанняВиконує одразу (runtime)Спочатку аналізує залежності
ІмпортиМожна в будь-якому місціЛише на верхньому рівні
Доступ до оточенняrequire, module, exports, __dirnameimport.meta.url, import/export
СумісністьБагато старих бібліотекСучасні бандлери та браузери
ПродуктивністьШвидше при локальних requireЕфективніше у великих проєктах

Одним реченням:

CommonJS - динамічна, синхронна, специфічна для Node.js модульна система, де імпорти виконуються під час запуску.
ES Modules - статична, асинхронна, стандартизована система, де залежності відомі заздалегідь, а імпорт створює живі посилання.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.