ES6 Модули: лучшие практики импорта и экспорта

Система модулей ES6 (import/export) стала стандартом. Она статическая, что позволяет инструментам сборки (Webpack, Vite) делать tree-shaking (удаление неиспользуемого кода). Однако есть нюансы использования.

Именованный vs Default экспорт

  • Default: Один на файл. Удобно для компонентов.
  • Named: Много на файл. Удобно для утилит.
// utils.js
export const add = (a, b) => a + b; // Named
export default class User {};       // Default

// main.js
import User, { add } from './utils.js';

Рекомендация: используйте именованные экспорты по умолчанию. Это упрощает рефакторинг и поиск использования (grep по имени функции находит и экспорт, и импорт). Default экспорт часто приводит к импорту с произвольными именами (import Foo from './Bar'), что запутывает.

Круговые зависимости

ES6 модули поддерживают круговые зависимости лучше, чем CommonJS, но это все равно плохой запах. Если модуль A импортирует B, а B импортирует A, порядок инициализации может привести к тому, что один из импортов будет undefined.
Старайтесь выносить общий код в третий модуль C, от которого зависят и A, и B.

Динамический импорт

Для оптимизации загрузки (Code Splitting) используйте динамический import(). Он возвращает Promise.

button.addEventListener('click', async () => {
  const module = await import('./heavy-module.js');
  module.init();
});

Это позволяет загружать код только тогда, когда он действительно нужен пользователю, ускоряя начальную загрузку приложения (FCP).