Именование динамических чанков

Динамические чанки в Rollup появляются в результате использования динамического импорта (import()), когда код разбивается на отдельные части, загружаемые по требованию. Каждый такой модульный фрагмент превращается в отдельный файл при сборке. В отличие от статических entry-бандлов, их количество и структура зависят от графа зависимостей и точек разделения кода.

Ключевая особенность динамических чанков заключается в том, что их имена не фиксированы заранее — они генерируются на этапе сборки, исходя из конфигурации и правил именования.


Базовый механизм генерации имени чанка

При встрече динамического импорта Rollup:

import('./module.js')

создаёт отдельный чанк. Если не задано дополнительной конфигурации, имя определяется автоматически:

  • используется имя входного модуля или
  • вычисляется из пути модуля
  • дополняется хешем (если включено хеширование)

По умолчанию результат выглядит примерно так:

module-a1b2c3.js

Хеш в имени гарантирует уникальность и помогает кэшированию.


Параметр output.chunkFileNames

Основной инструмент управления именованием динамических чанков — output.chunkFileNames.

export default {
  output: {
    chunkFileNames: '[name]-[hash].js'
  }
}

Доступные шаблонные переменные:

  • [name] — логическое имя чанка
  • [hash] — хеш содержимого
  • [format] — формат сборки (cjs, esm и др.)
  • [extname] — расширение файла

Поведение [name]

[name] формируется не всегда очевидно. Rollup пытается вывести его из:

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

Если имя не удаётся определить, используется fallback вроде chunk.


Управление именем через manualChunks

Хотя manualChunks чаще используется для группировки, он напрямую влияет на именование динамических чанков.

export default {
  output: {
    manualChunks(id) {
      if (id.includes('node_modules')) {
        return 'vendor'
      }
    }
  }
}

Результат:

vendor-[hash].js

Влияние возврата строки

Возвращаемая строка становится:

  • идентификатором чанка
  • базой для [name]
  • стабильным именем при последующих сборках

Это один из самых надёжных способов контролировать динамические чанки.


Детерминированные имена и стабильность сборок

Для production-сборок важно избегать «прыгающих» имён.

Проблема нестабильности

Без контроля именования возможны ситуации:

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

Использование предсказуемого имени через структуру модулей

Rollup может формировать имя чанка на основе относительного пути:

src/components/Button.js → components-Button.js

Но при глубокой вложенности это приводит к:

  • длинным именам
  • дублированию сегментов
  • неоднозначности

Поэтому такой подход обычно комбинируется с manualChunks.


Контроль через output.entryFileNames и взаимодействие с чанками

Хотя entryFileNames напрямую не влияет на динамические чанки, он важен для согласованности именования:

export default {
  output: {
    entryFileNames: '[name]-entry.js',
    chunkFileNames: '[name]-chunk-[hash].js'
  }
}

Это создаёт единый стиль:

  • entry-файлы отделены
  • динамические чанки визуально различимы
  • проще анализировать бандл

Проблема «безымянных» чанков

Если Rollup не может определить имя, он создаёт техническое имя:

chunk-abc123.js

Это происходит, когда:

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

Косвенное именование через экспортные границы

Rollup может использовать имя экспортируемого модуля как основу чанка.

Пример:

export function init() {}

При динамическом импорте:

import('./init.js')

возможное имя чанка:

init-[hash].js

Если же модуль агрегирует несколько экспортов, имя становится менее предсказуемым.


Стратегии именования динамических чанков

Стратегия 1: функциональная группировка

Разделение по смыслу:

manualChunks(id) {
  if (id.includes('chart')) return 'charts'
  if (id.includes('auth')) return 'auth'
}

Результат:

charts-[hash].js
auth-[hash].js

Плюсы:

  • читаемость
  • логическая структура

Минусы:

  • требует поддержки правил

Стратегия 2: vendor-ядро

Вынос зависимостей:

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

Результат:

vendor-[hash].js

Используется для:

  • React
  • Vue
  • Lodash
  • Axios

Стратегия 3: модульное именование по файлам

chunkFileNames: 'chunks/[name]-[hash].js'

Преимущества:

  • физическое разделение
  • удобство анализа
  • простая навигация в dist

Влияние dynamic import на имя чанка

Каждый import() создаёт отдельную точку разбиения:

button.oncl ick = async () => {
  const module = await import('./heavy.js')
}

Имя чанка зависит от:

  • пути ./heavy.js
  • правил manualChunks
  • шаблона chunkFileNames

Глубокая цепочка зависимостей

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

A → import B → import C

Rollup:

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

Если же B и C используются в других чанках, происходит пересечение и дополнительное разделение.


Коллизии имён и их предотвращение

При сложной конфигурации возможны конфликты:

  • одинаковые [name]
  • пересечение manualChunks
  • неоднозначные пути

Решения:

  • использовать [hash]
  • структурировать manualChunks по доменам
  • избегать слишком общих ключей

Использование функции chunkFileNames

Шаблон можно заменить функцией:

export default {
  output: {
    chunkFileNames(chunkInfo) {
      return `chunks/${chunkInfo.name}-${chunkInfo.hash}.js`
    }
  }
}

chunkInfo содержит:

  • name
  • facadeModuleId
  • modules
  • isDynamicEntry
  • imports

Это позволяет строить сложную логику именования.


Привязка имен к доменной структуре приложения

Для крупных приложений применяются правила:

  • UI → ui-*
  • API → api-*
  • Pages → page-*
  • Features → feature-*

Пример:

ui-button-[hash].js
feature-auth-login-[hash].js

Это делает структуру сборки сопоставимой с архитектурой проекта.


Влияние preserveModules на именование

При включении:

preserveModules: true

Rollup перестаёт агрессивно объединять модули.

Имена становятся ближе к файловой структуре:

src/utils/math.js → utils/math.js

Динамические чанки при этом могут дробиться сильнее, но становятся более предсказуемыми.


Практическая модель стабильного именования

Комбинированная конфигурация:

export default {
  output: {
    manualChunks(id) {
      if (id.includes('node_modules')) return 'vendor'
    },
    chunkFileNames: 'chunks/[name]-[hash].js'
  }
}

Поведение:

  • vendor остаётся стабильным
  • локальные чанки получают имя из файла
  • хеш обеспечивает уникальность

Ошибки проектирования именования чанков

Типичные проблемы:

  • слишком много manualChunks → фрагментация
  • отсутствие hash → проблемы кэша
  • слишком общий [name] → конфликты
  • отсутствие структуры → невозможность анализа бандла

Поведение Rollup при пересборке

При изменении кода:

  • пересчитывается граф зависимостей
  • меняются только затронутые чанки
  • [hash] обновляется точечно

Если имена не детерминированы, пересборка может затронуть больше файлов, чем необходимо.


Стабильность как основная цель именования

Именование динамических чанков в Rollup всегда балансирует между:

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

На практике наиболее устойчивый результат достигается комбинацией:

  • manualChunks для логики разбиения
  • chunkFileNames для структуры
  • [hash] для кэш-устойчивости