Система модулей 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).