Ручное управление чанками через Entry и dependOn

Webpack формирует граф зависимостей, начиная с одной или нескольких точек входа. Каждая точка входа (entry) определяет, с какого файла начинается сборка отдельного фрагмента приложения. При этом итоговый бандл представляет собой набор чанков, которые могут пересекаться по зависимостям.

Базовая конфигурация с одной точкой входа выглядит следующим образом:

module.exports = {
  entry: './src/index.js',
};

В этом случае Webpack строит единое дерево зависимостей и формирует один основной чанк (или несколько, если подключены дополнительные плагины оптимизации).

При переходе к нескольким точкам входа структура становится более сложной:

module.exports = {
  entry: {
    app: './src/app.js',
    admin: './src/admin.js',
  },
};

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


Проблема дублирования зависимостей при нескольких entry

При наличии нескольких entry Webpack рассматривает каждый вход как отдельное дерево модулей. Если оба дерева используют одни и те же библиотеки, например:

// app.js
import lodash from 'lodash';

// admin.js
import lodash from 'lodash';

то без оптимизации lodash будет включён в оба бандла. Это приводит к:

  • увеличению размера итоговой сборки;
  • повторной загрузке одинакового кода;
  • ухудшению производительности на клиенте.

Решение этой проблемы заключается в явном или автоматическом выделении общих зависимостей. Одним из базовых механизмов управления этим процессом является использование dependOn в конфигурации entry.


Механизм dependOn как ручное управление общими зависимостями

dependOn позволяет явно указать, что одна точка входа зависит от другой. Это создаёт промежуточный чанк, содержащий общие модули, который переиспользуется несколькими entry.

Простейшая конфигурация:

module.exports = {
  entry: {
    shared: './src/shared.js',
    app: {
      import: './src/app.js',
      dependOn: 'shared',
    },
    admin: {
      import: './src/admin.js',
      dependOn: 'shared',
    },
  },
};

В данной структуре:

  • shared формирует отдельный чанк;
  • app и admin не включают общие зависимости внутрь себя;
  • общий код загружается один раз через shared.

Логика формирования графа зависимостей при dependOn

Webpack интерпретирует dependOn как указание на предкомпилированный слой зависимостей. При построении графа:

  1. Сначала обрабатываются entry без зависимостей.
  2. Формируются чанки, указанные в dependOn.
  3. Последовательно добавляются зависимые entry.
  4. Общие модули исключаются из дочерних чанков.

Таким образом создаётся иерархия:

shared
 ├── app
 └── admin

Важно, что shared не является абстрактным псевдонимом — это полноценный чанк, который может быть загружен отдельно или автоматически подтянут зависимыми entry.


Ограничения использования dependOn

Механизм имеет ряд архитектурных ограничений, которые важно учитывать при проектировании сборки.

1. Отсутствие динамической агрегации

dependOn задаётся статически. Это означает, что:

  • нельзя изменять зависимости во время выполнения;
  • нельзя автоматически объединять пересекающиеся модули без явного указания.

2. Жёсткая иерархия

Каждый entry может зависеть только от заранее определённых чанков. Попытка создать циклические зависимости приводит к ошибке сборки.

entry: {
  a: { import: './a.js', dependOn: 'b' },
  b: { import: './b.js', dependOn: 'a' }, // ошибка
}

3. Ручное сопровождение структуры

При росте проекта необходимо поддерживать актуальность shared-чанков вручную. Это может привести к архитектурной инерции, если структура зависимостей часто меняется.


Использование массива dependOn для нескольких общих чанков

dependOn поддерживает массив значений, что позволяет строить более сложные композиции:

module.exports = {
  entry: {
    vendor: './src/vendor.js',
    runtime: './src/runtime.js',

    app: {
      import: './src/app.js',
      dependOn: ['vendor', 'runtime'],
    },

    admin: {
      import: './src/admin.js',
      dependOn: ['vendor', 'runtime'],
    },
  },
};

В этом случае формируется несколько уровней абстракции:

  • vendor — сторонние библиотеки;
  • runtime — инфраструктурный код;
  • app и admin — прикладные модули.

Разделение ответственности между entry и shared-чанками

Архитектурно важно различать назначение entry и зависимых чанков:

  • entry — точки инициализации приложения;
  • shared chunks — переиспользуемый код;
  • dependOn — механизм декларации связи между ними.

Типичная ошибка — использование entry как универсального контейнера для всего общего кода. Это приводит к:

  • разрастанию entry-чанков;
  • ухудшению кешируемости;
  • дублированию runtime-логики.

Влияние dependOn на кэширование

Разделение через dependOn напрямую влияет на стратегию кеширования в браузере.

Если выделен стабильный shared-чанк:

  • его хэш меняется реже;
  • браузер переиспользует закэшированную версию;
  • обновления затрагивают только прикладные чанки.

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


Сочетание dependOn и оптимизации SplitChunks

Хотя dependOn решает задачу ручного выделения общих зависимостей, он часто используется в комбинации с SplitChunksPlugin.

Разница заключается в подходе:

  • dependOn — ручное управление графом entry;
  • SplitChunksPlugin — автоматическое выделение общих модулей.

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

  • пересечения чанков;
  • неоднозначное распределение модулей;
  • усложнение анализа бандла.

Практическая модель построения архитектуры entry-графа

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

  1. Базовый runtime-чанк:

    • полифилы;
    • глобальные инициализации.
  2. Vendor-чанк:

    • сторонние библиотеки.
  3. Feature-чанки:

    • app;
    • admin;
    • dashboard.
  4. Ленивая подгрузка:

    • динамические импорты внутри feature-чанков.

В такой архитектуре dependOn используется для фиксации верхнего уровня графа, обеспечивая стабильность структуры и предсказуемость загрузки.


Поведение runtime при использовании dependOn

Webpack runtime отвечает за связывание чанков в браузере. При использовании dependOn он:

  • отслеживает зависимости между entry;
  • загружает shared-чанки до зависимых entry;
  • предотвращает дублирование модулей в рантайме;
  • поддерживает корректную инициализацию порядка исполнения.

Если shared-чанк не загружен, выполнение зависимого entry откладывается до его загрузки.


Ошибки конфигурации и типичные антишаблоны

На практике часто встречаются следующие проблемы:

Дублирование shared-логики

Когда один и тот же модуль указывается в нескольких dependOn-цепочках, но не вынесен в общий слой.

Избыточное дробление entry

Создание множества entry ради мелких частей функционала вместо использования динамических импортов.

Игнорирование реальной графовой структуры

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


Роль dependOn в эволюции сборки Webpack

Механизм dependOn является переходным инструментом между простой моделью «один entry — один бандл» и современными графовыми подходами к сборке.

Он позволяет:

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

При этом он сохраняет прозрачность архитектуры, так как явно отражает связи между точками входа без скрытой магии автоматических оптимизаций.