ESM проти CommonJS
1. Коротко: головна ідея
| Система | Основна концепція |
|---|---|
| CommonJS (CJS) | Модулі завантажуються під час виконання (runtime), синхронно, порядково. |
| ES Modules (ESM) | Модулі аналізуються на етапі компіляції (parse-time), завантажуються асинхронно, із суворими залежностями й "живими" імпортами. |
2. Філософія систем
CommonJS
Створювався для Node.js (2009 рік), щоб виконувати JS на сервері.
- Усе синхронно (адже файли - на локальному диску);
- Кожен файл - ізольований модуль;
- Код виконується згори вниз при першому
require(); - Імпорти - це копія значень, не "живі посилання".
Приклад:
// 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, тобто "живі" посилання, а не копії.
Приклад:
// counter.js
export let count = 0;
export function inc() { count++; }
// app.js
import { count, inc } from './counter.js';
inc();
console.log(count); // 1 (значення оновилося)3. Концептуальні відмінності
| Концепція | CommonJS | ES Modules |
|---|---|---|
| Час завантаження | Під час виконання (runtime) | До виконання (parse-time) |
| Імпорти | Динамічні (require()) | Статичні (import) |
| Тип зв'язку | Значення копіюються | Живі посилання (live bindings) |
| Порядок виконання | Згори вниз | Визначається залежностями |
| Асинхронність | Синхронно | Асинхронно |
| Оптимізація | Неможлива (runtime) | Можливий tree-shaking |
| Контекст | this = module.exports | this = undefined |
| Кешування | Після першого виклику | Є, але з "живими" значеннями |
| Екосистема | Node.js, старі пакети | Сучасний JS, браузери, Node 13+ |
| Архітектура | Імперативна | Декларативна |
| Сумісність | Тільки сервер | Універсальна (сервер + клієнт) |
4. Різниця в момент завантаження й компіляції
CommonJS:
-
Виконується рядок
require()→ читається файл → виконується код. -
Тому шлях може бути змінною:
javascriptconst lib = require(condition ? './a' : './b');працює, тому що це звичайна функція.
ES Modules:
-
Імпорти мають бути у верхній частині файлу і не можуть бути динамічними:
javascriptimport x from './a'; // ок if (cond) import x from './b'; // помилка -
Node.js і браузер знають наперед, які залежності потрібні, і можуть завантажувати їх паралельно до виконання коду.
5. Різниця у структурі модуля
Node.js "обгортає" CommonJS-модуль у внутрішню функцію:
(function (exports, require, module, __filename, __dirname) {
// твій код
});Тому є:
requiremodule.exports__dirname__filename
У ESM цього немає:
- замість
__dirname→import.meta.url requireнедоступний
6. Поведінка при кешуванні
У CommonJS модуль виконується один раз і кешується:
const a = require('./a');
const b = require('./a');
console.log(a === b); // trueУ ESM - теж кеш, але імпорти лишаються живими (якщо змінні експортовані як let/const):
import { counter } from './a.js';Якщо модуль оновлює значення - усі імпорти бачать оновлення.
7. Tree-shaking та оптимізація
Tree-shaking - видалення невикористовуваного коду (у Webpack, Rollup, Vite тощо)
- CJS: неможливо, бо імпорти динамічні;
- ESM: можливо, бо залежності статичні.
Приклад:
import { used } from './utils.js'; // збирач знає, що потрібен лише used8. Сумісність
| Сценарій | CommonJS | ES Modules |
|---|---|---|
| Використовувати старі пакети | Так | Іноді потрібен createRequire() |
| SSR / сервер | Відмінно | Відмінно (Node 13+) |
| Браузери | Немає | Так |
| Tree-shaking | Немає | Так |
| Top-level await | Немає | Так |
9. "Живі" vs "мертві" імпорти (приклад)
CommonJS:
// 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:
// counter.mjs
export let count = 0;
export function inc() { count++; }
// app.mjs
import { count, inc } from './counter.mjs';
inc();
console.log(count); // 1 (жива посилання)10. Концептуально - ключові відмінності
| Параметр | CommonJS | ESM |
|---|---|---|
| Основа | Node.js (сервер) | Стандарт ECMAScript |
| Модель виконання | Виконує одразу (runtime) | Спочатку аналізує залежності |
| Імпорти | Можна будь-де | Тільки на верхньому рівні |
| Доступ до середовища | require, module, exports, __dirname | import.meta.url, import/export |
| Сумісність | Багато старих бібліотек | Сучасні збирачі й браузери |
| Продуктивність | Швидше при локальних require | Ефективніше при великих проєктах |
Одним реченням
CommonJS - динамічна, синхронна, специфічна для Node.js модульна система, де імпорти виконуються під час запуску. ES Modules - статична, асинхронна, стандартизована система, де залежності відомі заздалегідь, а імпорт створює живі посилання.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.