3. ES Modules (ESM)

ES Modules — современный стандарт модулей JavaScript. Используется в браузерах и Node.js. Синтаксис: import / export.
CommonJS vs ESM
Заголовок раздела «CommonJS vs ESM»// CommonJS (старый стиль)const express = require('express');module.exports = { myFunc };
// ESM (современный стиль)import express from 'express';export { myFunc };export default myFunc;Включение ESM в Node.js
Заголовок раздела «Включение ESM в Node.js»Два способа:
// package.json — включить ESM для всего проекта{ "type": "module"}// Или использовать расширение .mjs для ESM файлов// Тогда .js остаётся CommonJS, .mjs — ESMСинтаксис экспорта
Заголовок раздела «Синтаксис экспорта»// Named export — именованный экспортexport function add(a, b) { return a + b;}
export const PI = 3.14159;
export class Calculator { constructor() { this.result = 0; } add(n) { this.result += n; return this; }}
// Default export — экспорт по умолчанию (один на файл)export default function multiply(a, b) { return a * b;}Синтаксис импорта
Заголовок раздела «Синтаксис импорта»// Named importsimport { add, PI } from './math.js';// Внимание: расширение .js обязательно в ESM!
// Default importimport multiply from './math.js';
// Совмещённый импортimport multiply, { add, PI } from './math.js';
// Импорт всего как объектimport * as math from './math.js';console.log(math.add(2, 3));console.log(math.PI);console.log(math.default(4, 5)); // default export
// Переименование при импортеimport { add as sum, PI as pi } from './math.js';
// Динамический импорт (работает в CommonJS тоже!)const module = await import('./math.js');Top-level await в ESM
Заголовок раздела «Top-level await в ESM»// В ESM можно использовать await на верхнем уровне!// В CommonJS это недоступно.
import { readFile } from 'fs/promises';
// Прямо на верхнем уровне — без async функцииconst config = JSON.parse(await readFile('./config.json', 'utf8'));console.log(config);
// Динамический выбор конфигаconst env = process.env.NODE_ENV ?? 'development';const { default: settings } = await import(`./config.${env}.js`);Import.meta
Заголовок раздела «Import.meta»// В ESM нет __dirname и __filename// Используй import.meta.url
import { fileURLToPath } from 'url';import { dirname, join } from 'path';
const __filename = fileURLToPath(import.meta.url);const __dirname = dirname(__filename);
// Теперь можно использовать __dirname как раньшеconst dataPath = join(__dirname, 'data', 'users.json');Импорт JSON и других типов
Заголовок раздела «Импорт JSON и других типов»// Импорт JSON (Node.js 20+)import data from './data.json' assert { type: 'json' };console.log(data.users);
// Импорт Node.js встроенных модулейimport { readFile, writeFile } from 'node:fs/promises';import { join } from 'node:path';// Префикс node: — явно указывает что это встроенный модульRe-export — перепередача экспортов
Заголовок раздела «Re-export — перепередача экспортов»// utils/index.js — "barrel" файл, собирает всё из папки
// Re-export namedexport { add, multiply } from './math.js';export { formatDate } from './date.js';export { capitalize } from './string.js';
// Re-export с переименованиемexport { default as Calculator } from './calculator.js';
// Re-export всегоexport * from './helpers.js';// Теперь можно импортировать всё из одного местаimport { add, formatDate, capitalize } from './utils/index.js';Смешивание CommonJS и ESM
Заголовок раздела «Смешивание CommonJS и ESM»// В ESM модуле можно импортировать CommonJS пакетыimport express from 'express'; // express — CommonJSimport lodash from 'lodash'; // lodash — CommonJS// Работает через автоматическую конвертацию
// В CommonJS нельзя напрямую require() ESM файлы// Нужен динамический import():async function loadESMModule() { const { default: something } = await import('./esm-module.js'); return something;}Когда использовать что?
Заголовок раздела «Когда использовать что?»CommonJS ESM────────────────────────── ──────────────────────────Старые проекты Новые проектыrequire() уже есть Хочешь современный стильПакеты только CJS Top-level await нужен__dirname доступен Tree-shaking (bundlers) Браузер + Node.js кодПрактика
Заголовок раздела «Практика»- Создай проект с
"type": "module"в package.json - Создай модуль
services/user.jsс ESM экспортами - Используй top-level await для загрузки конфига из JSON
- Создай barrel файл
utils/index.jsс re-export - Попробуй динамический
import()для условной загрузки модуля