Зависимости, общие между несколькими точками входа

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

При конфигурации Webpack с несколькими точками входа формируется отдельный граф зависимостей для каждой из них. Например:

  • entry: app
  • entry: admin

Если оба модуля используют lodash, moment или общий внутренний модуль, Webpack по умолчанию включает эти зависимости в каждый итоговый бандл.

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

В результате формируется избыточная структура:

  • app.bundle.js → содержит lodash + app код
  • admin.bundle.js → содержит lodash + admin код

Повторяющийся код увеличивает размер загрузки и ухудшает кэширование.

Роль разделения общих модулей

Разделение общих зависимостей позволяет:

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

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

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

Основной механизм устранения дублирования в Webpack — SplitChunksPlugin, встроенный в систему оптимизации.

Базовая конфигурация:

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

При таком режиме Webpack анализирует все зависимости и автоматически выделяет общие модули в отдельные чанки.

Механика работы splitChunks

Алгоритм разделения включает несколько этапов:

  1. Построение графа зависимостей для всех entry
  2. Анализ повторяющихся модулей
  3. Определение порогов для извлечения
  4. Формирование отдельных chunk-файлов

По умолчанию учитываются параметры:

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

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

Cache Groups как инструмент управления логикой разделения

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

Пример настройки:

optimization: {
  splitChunks: {
    chunks: 'all',
    cacheGroups: {
      vendors: {
        test: /[\\/]node_modules[\\/]/,
        name: 'vendors',
        chunks: 'all'
      }
    }
  }
}

Здесь все зависимости из node_modules выносятся в отдельный файл vendors.

Это приводит к следующей структуре:

  • vendors.js → сторонние библиотеки
  • app.js → бизнес-логика приложения
  • admin.js → административная логика

Разделение внутренних общих модулей

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

Пример:

cacheGroups: {
  common: {
    test: /[\\/]src[\\/]shared[\\/]/,
    name: 'common',
    minChunks: 2,
    chunks: 'all'
  }
}

Параметр minChunks: 2 означает, что модуль должен использоваться минимум в двух точках входа, чтобы попасть в общий бандл.

Это позволяет:

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

Контроль размера и условий извлечения

Webpack предоставляет несколько параметров для тонкой настройки:

minSize и maxSize

minSize: 20000

Модуль будет вынесен в отдельный chunk только если его размер превышает заданный порог.

maxSize: 50000

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

minChunks

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

minChunks: 2

Используется для контроля уровня «общности» кода.

priority

Позволяет задавать приоритет cacheGroup:

priority: 10

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

Различие между синхронными и асинхронными чанками

Параметр chunks определяет область анализа:

  • initial — только синхронные зависимости entry points
  • async — только динамически загружаемые модули
  • all — объединённый анализ

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

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

Выделение общих зависимостей существенно улучшает работу кэша браузера.

Если библиотека вынесена в отдельный файл:

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

Это особенно важно при использовании долгоживущих кэшей с contenthash.

Пример:

  • vendors.8fd3a1.js — неизменный при обновлении логики приложения
  • app.3ac91d.js — изменяется при каждом релизе

Ошибки при настройке общих зависимостей

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

Слишком агрессивное дробление

При слишком маленьком minSize создаётся большое количество файлов, что увеличивает накладные расходы на загрузку.

Игнорирование vendor separation

Отсутствие выделения node_modules приводит к повторной загрузке тяжёлых библиотек.

Неправильный minChunks

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

Конфликты cacheGroups

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

Стратегии оптимальной структуры

В многоточечных приложениях обычно применяется комбинация:

  • vendors chunk для внешних зависимостей
  • common chunk для внутренних модулей
  • runtime chunk для служебного кода Webpack

Пример:

optimization: {
  runtimeChunk: 'single',
  splitChunks: {
    chunks: 'all',
    cacheGroups: {
      vendors: {
        test: /[\\/]node_modules[\\/]/,
        name: 'vendors',
        priority: 20
      },
      common: {
        test: /[\\/]src[\\/]shared[\\/]/,
        name: 'common',
        minChunks: 2,
        priority: 10
      }
    }
  }
}

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

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

При использовании import() Webpack автоматически создаёт отдельные чанки для асинхронных модулей. Если такие модули содержат общие зависимости, они могут быть дополнительно вынесены в shared chunks.

Это позволяет:

  • снижать размер initial bundle
  • загружать функциональность по требованию
  • повторно использовать код между динамическими модулями

Взаимодействие с tree shaking

Разделение общих зависимостей тесно связано с tree shaking:

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

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

Практическое поведение в реальных проектах

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

  • дублирование lodash, axios, moment
  • повторение UI-библиотек
  • увеличение общего объёма загрузки на десятки процентов

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

  • уменьшается общий transfer size
  • повышается эффективность CDN
  • ускоряется переход между страницами
  • сокращается время холодного старта приложения