manualChunks как функция

Базовая идея разбиения кода на чанки

В Rollup механизм code splitting опирается на граф модулей: каждый импорт формирует зависимости, а итоговая сборка может быть разделена на отдельные файлы (chunks). Это позволяет оптимизировать загрузку, кеширование и уменьшить первоначальный бандл.

Опция output.manualChunks управляет тем, как именно Rollup группирует модули в чанки. Когда она задана как функция, появляется возможность программно контролировать структуру сборки на уровне каждого модуля.


Сигнатура функции manualChunks

Функция вызывается для каждого модуля в графе:

manualChunks(id, meta)

Где:

  • id — абсолютный путь к модулю или виртуальный идентификатор
  • meta — объект с дополнительной информацией о модуле (в частности, getModuleInfo)

Возвращаемое значение:

  • string — имя чанка, в который нужно поместить модуль
  • undefined — оставить решение Rollup (автоматическое размещение)
  • в некоторых сценариях допустим возврат массива строк, но основной и стабильный вариант — строка или undefined

Поведение Rollup при использовании функции

Когда manualChunks задан как функция, Rollup переходит в режим ручного распределения модулей. Логика выглядит так:

  1. Для каждого модуля вызывается функция
  2. Возвращаемое значение определяет чанк
  3. Модули с одинаковым именем чанка группируются вместе
  4. Если возвращено undefined, Rollup применяет стандартные алгоритмы (например, vendor splitting при динамическом импорте)

Важно: функция не влияет на порядок выполнения кода напрямую, она влияет только на структуру файлов.


Простое разделение vendor-кода

Один из самых распространённых сценариев — отделение зависимостей из node_modules.

export default {
  input: 'src/main.js',
  output: {
    dir: 'dist',
    format: 'esm',
    manualChunks(id) {
      if (id.includes('node_modules')) {
        return 'vendor';
      }
    }
  }
};

Результат:

  • весь сторонний код попадает в vendor.js
  • приложение остаётся в основном чанке

Такой подход улучшает кеширование: зависимости меняются реже, чем собственный код проекта.


Разделение по библиотекам

Более точный контроль достигается разбиением по конкретным пакетам.

manualChunks(id) {
  if (id.includes('node_modules')) {
    if (id.includes('lodash')) return 'lodash';
    if (id.includes('react')) return 'react';
    return 'vendor';
  }
}

Так формируются отдельные чанки:

  • lodash
  • react
  • общий vendor

Это особенно полезно в больших приложениях, где разные зависимости обновляются независимо.


Группировка по функциональным областям

Функция manualChunks позволяет реализовать архитектурное разделение кода.

manualChunks(id) {
  if (id.includes('/src/pages/')) {
    return 'pages';
  }

  if (id.includes('/src/components/')) {
    return 'components';
  }

  if (id.includes('/src/utils/')) {
    return 'utils';
  }
}

В результате:

  • страницы оказываются в отдельном чанке
  • компоненты изолированы
  • утилиты выделены отдельно

Такой подход помогает при частичной загрузке приложения и улучшает параллелизм загрузки.


Использование getModuleInfo

Второй аргумент функции даёт доступ к информации о модуле:

manualChunks(id, { getModuleInfo }) {
  const info = getModuleInfo(id);
}

Через getModuleInfo можно:

  • анализировать импорты модуля
  • определять зависимости
  • строить более сложные правила группировки

Пример выделения общих зависимостей:

manualChunks(id, { getModuleInfo }) {
  const info = getModuleInfo(id);

  if (info && info.importers && info.importers.length > 5) {
    return 'common';
  }
}

Здесь модули, которые используются в нескольких местах, попадают в общий чанк.


Динамическое разбиение по доменам логики

В сложных приложениях часто используется доменная архитектура.

manualChunks(id) {
  if (id.includes('/auth/')) return 'auth';
  if (id.includes('/billing/')) return 'billing';
  if (id.includes('/dashboard/')) return 'dashboard';
}

Такой подход позволяет:

  • загружать функциональные блоки отдельно
  • уменьшать начальный payload
  • изолировать изменения в конкретных областях

Приоритет manualChunks над автоматикой Rollup

Когда функция возвращает имя чанка, Rollup перестаёт использовать стандартную эвристику для данного модуля. Это означает:

  • нет автоматического объединения по графу
  • контроль полностью переходит разработчику
  • возможны пересечения, если логика плохо продумана

Если несколько правил возвращают разные значения для одного и того же модуля, учитывается первое сработавшее условие в коде функции.


Ограничения и типичные ошибки

1. Дублирование модулей

Если разные условия возвращают разные чанки для связанных модулей, можно получить:

  • дублирование кода
  • увеличение размера бандла

2. Слишком дробное разбиение

Чрезмерное количество чанков приводит к:

  • росту числа HTTP-запросов
  • ухудшению времени загрузки на слабых сетях
  • увеличению overhead загрузчика

3. Нестабильные пути

Использование относительных путей без нормализации может ломать сборку:

id.includes('../utils/')

Такой код чувствителен к структуре проекта и легко становится источником ошибок.


Комбинирование с dynamic import

manualChunks особенно эффективно работает вместе с динамическими импортами:

const module = await import('./features/analytics.js');

Rollup:

  • создаёт отдельный chunk для динамического импорта
  • затем применяет manualChunks для группировки зависимостей

В результате можно контролировать как точку загрузки, так и структуру зависимостей внутри неё.


Паттерн vendor + app + shared

Один из устойчивых подходов:

manualChunks(id) {
  if (id.includes('node_modules')) {
    return 'vendor';
  }

  if (id.includes('/src/shared/')) {
    return 'shared';
  }
}

Итоговая структура:

  • vendor — внешние зависимости
  • shared — переиспользуемые модули
  • основной chunk — бизнес-логика

Производительность и кеширование

Правильная стратегия manualChunks влияет на:

  • стабильность хэшей файлов
  • эффективность CDN кеша
  • частоту повторной загрузки зависимостей

Чем стабильнее границы чанков, тем выше эффективность кеширования.


Сложные стратегии группировки

В продвинутых конфигурациях используется анализ графа:

manualChunks(id, { getModuleInfo }) {
  const info = getModuleInfo(id);

  if (!info) return;

  const isUsedOnlyByAuth = info.importers?.every(i =>
    i.includes('/auth/')
  );

  if (isUsedOnlyByAuth) {
    return 'auth-only';
  }
}

Такие подходы позволяют строить почти статический анализ зависимостей прямо на этапе сборки.


Влияние на архитектуру приложения

Использование функции manualChunks фактически превращает конфигурацию Rollup в слой архитектурного проектирования:

  • определяются границы модулей
  • формируются зоны ответственности
  • задаётся стратегия загрузки приложения

Это делает сборку не просто техническим этапом, а частью проектирования системы.