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. "Живі" vs "мертві" імпорти (приклад)

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

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