Rollup строит граф модулей на основе ES-модулей и стремится объединять зависимости в минимально возможное количество чанков. При этом общие зависимости — модули, используемые в нескольких частях приложения — становятся ключевым фактором влияния на итоговую структуру бандла.
В отличие от бандлеров с агрессивным рантайм-чанкингом, Rollup ориентирован на статический анализ. Это означает, что решение о том, как разделять или объединять общие зависимости, принимается на этапе сборки, а не выполнения.
Дублирование модулей появляется в нескольких типичных ситуациях:
Особенно часто дублируются крупные сторонние библиотеки, если они импортируются из разных частей кода, но Rollup не видит возможности объединить их в общий чанк.
Tree-shaking в Rollup напрямую влияет на количество общих зависимостей. Поскольку анализируется только используемый экспорт, один и тот же модуль может быть урезан до разных подмножеств в зависимости от контекста использования.
Это приводит к ситуации, когда:
При слабой модульной структуре это превращается в скрытое дублирование логики.
Оптимизация начинается с выстраивания явной политики формирования чанков. Rollup не навязывает фиксированную модель vendor/app, поэтому её необходимо задавать через конфигурацию.
Основные подходы:
Ключевой принцип заключается в том, чтобы одинаковые зависимости попадали в одинаковые чанки при любых сценариях сборки.
Механизм output.manualChunks является основным
инструментом управления общими зависимостями.
Он позволяет явно определить, какие модули должны быть сгруппированы вместе:
export default {
output: {
manualChunks(id) {
if (id.includes('node_modules')) {
return 'vendor';
}
}
}
};
В этом случае все зависимости из node_modules
объединяются в единый чанк, что устраняет дублирование библиотечного
кода между входными точками.
Более сложная стратегия учитывает тип библиотеки:
manualChunks(id) {
if (id.includes('node_modules')) {
if (id.includes('react')) return 'react-vendor';
if (id.includes('lodash')) return 'utils-vendor';
return 'vendor';
}
}
Такой подход снижает вероятность того, что крупные зависимости будут пересекаться между чанками из-за частичного использования.
Оптимизация общих зависимостей внутри проекта требует не только работы с внешними библиотеками, но и с внутренними модулями.
Практика группировки по доменам:
Пример:
manualChunks(id) {
if (id.includes('/src/api/')) return 'api';
if (id.includes('/src/ui/')) return 'ui';
if (id.includes('/src/auth/')) return 'auth';
}
Это уменьшает вероятность того, что один и тот же внутренний код будет повторно включён в разные точки входа.
import() в Rollup создаёт отдельные чанки, которые
становятся естественным механизмом разделения кода. Однако без контроля
они могут приводить к разрастанию числа общих зависимостей.
Типичная проблема возникает, когда несколько динамических импортов используют одни и те же библиотеки, но Rollup создаёт отдельные чанки для каждой ветки.
Оптимизация заключается в том, чтобы:
manualChunksОпция preserveModules изменяет стратегию сборки: вместо
объединения модулей в чанки сохраняется структура исходных файлов.
При включении:
Оптимизация в этом режиме смещается в сторону:
Один из устойчивых подходов — разделение кода на два уровня:
Такое разделение снижает вероятность дублирования, поскольку vendor-чанк становится стабильной точкой переиспользования.
При корректной настройке:
Rollup использует идентификаторы модулей для определения их уникальности. При изменении структуры импорта даже без изменения кода может нарушаться кеширование и пересобираться общий чанк.
Факторы, влияющие на стабильность:
Оптимизация требует минимизации нестабильных трансформаций и унификации путей.
Сложные проекты часто сталкиваются с ситуацией, когда одна и та же библиотека попадает в разные чанки в слегка отличающихся формах.
Причины:
Контроль осуществляется через:
Стабильность графа важнее агрессивного дробления кода. При оптимизации общих зависимостей важно добиваться предсказуемости структуры:
Это позволяет избежать эффекта «разбухания» бандла при небольших изменениях кода.
Чрезмерное дробление приводит к росту накладных расходов на загрузку модулей, тогда как чрезмерная агрегация увеличивает размер отдельных файлов.
Оптимизация общих зависимостей сводится к балансу:
Выбор стратегии зависит от характера приложения: библиотека, SPA или многопоточный multi-entry проект требуют разных схем группировки зависимостей.