Может кто-нибудь объяснить CommonsChunkPlugin Webpack

82

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

Итак, предположим, что у меня следующая конфигурация:

...
enrty : {
    entry1 : 'entry1.js', //which has 'jquery' as a dependency
    entry2 : 'entry2.js', //which has 'jquery as a dependency
    vendors : [
        'jquery',
        'some_jquery_plugin' //which has 'jquery' as a dependency
    ]
},
output: {
    path: PATHS.build,
    filename: '[name].bundle.js'
}
...

Если я свяжу без использования CommonsChunkPlugin

Я получу 3 новых файла пакета:

  • entry1.bundle.jsкоторый содержит полный код из entry1.jsи jqueryи содержит собственную среду выполнения
  • entry2.bundle.jsкоторый содержит полный код из entry2.jsи jqueryи содержит собственную среду выполнения
  • vendors.bundle.jsкоторый содержит полный код из jqueryи some_jquery_pluginи содержит собственную среду выполнения

Это явно плохо, потому что я потенциально загружу jqueryстраницу 3 раза, поэтому мы этого не хотим.

Если я собираю, используя CommonsChunkPlugin

В зависимости от того, какие аргументы я передаю CommonsChunkPlugin, произойдет одно из следующего:

  • СЛУЧАЙ 1: Если я пройду, у { name : 'commons' }меня будут следующие файлы пакета:

    • entry1.bundle.jsкоторый содержит полный код entry1.js, является требованием jqueryи не содержит среду выполнения
    • entry2.bundle.jsкоторый содержит полный код entry2.js, является требованием jqueryи не содержит среду выполнения
    • vendors.bundle.jsкоторый содержит полный код some_jquery_plugin, является требованием jqueryи не содержит среду выполнения
    • commons.bundle.jsкоторый содержит полный код jqueryи содержит среду выполнения

    Таким образом, мы получаем несколько меньших пакетов в целом, а среда выполнения содержится в commonsпакете. Довольно хорошо, но не идеально.

  • СЛУЧАЙ 2: Если я пройду, у { name : 'vendors' }меня будут следующие файлы пакета:

    • entry1.bundle.jsкоторый содержит полный код entry1.js, является требованием jqueryи не содержит среду выполнения
    • entry2.bundle.jsкоторый содержит полный код entry2.js, является требованием jqueryи не содержит среду выполнения
    • vendors.bundle.jsкоторый содержит полный код из jqueryи some_jquery_pluginи содержит среду выполнения.

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

  • СЛУЧАЙ 3: Если я проиграю, у { names : ['vendors', 'manifest'] }меня будут следующие файлы пакета:

    • entry1.bundle.jsкоторый содержит полный код entry1.js, является требованием jqueryи не содержит среду выполнения
    • entry2.bundle.jsкоторый содержит полный код entry2.js, является требованием jqueryи не содержит среду выполнения
    • vendors.bundle.jsкоторый содержит полный код из jqueryи some_jquery_pluginи не содержит среду выполнения
    • manifest.bundle.js который содержит требования для всех остальных пакетов и среду выполнения

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

Что я не понимаю / не уверен, что понимаю

  • В СЛУЧАЕ 2 почему мы получили vendorsпакет, содержащий как общий код ( jquery), так и все, что осталось от vendorsentry ( some_jquery_plugin)? Насколько я понимаю, CommonsChunkPluginон собирал общий код ( jquery), и, поскольку мы заставили его выводить его в vendorsпакет, он как бы «слил» общий код с vendorsпакетом (который теперь содержал только код из some_jquery_plugin). Подтвердите или объясните.

  • В СЛУЧАЕ 3 я не понимаю, что произошло, когда мы перешли { names : ['vendors', 'manifest'] }к плагину. Почему / как был сохранен vendorsпакет без изменений, содержащий оба jqueryи some_jquery_plugin, когда jqueryявно является общей зависимостью, и почему сгенерированный manifest.bundle.jsфайл был создан таким образом, как он был создан (требуя все другие пакеты и содержащий среду выполнения)?

Димитрис Карагианнис
источник
Для случая 1 я думаю, вам следует указать свойство minChunks.
Марко
10
Я так много узнал из вашего вопроса , большое спасибо!
Рафаэль Эйнг
1
Большое вам спасибо за вопрос и прояснение моих недоразумений по
Гленн Мохаммад
Может кто знает, как этот пример будет выглядеть в Webpack 4?
StalkAlex 08

Ответы:

105

Вот как это CommonsChunkPluginработает.

Общий блок "получает" модули, общие для нескольких блоков входа. Хороший пример сложной конфигурации можно найти в репозитории Webpack .

Он CommonsChunkPluginзапускается на этапе оптимизации Webpack, что означает, что он работает в памяти, непосредственно перед тем, как блоки будут запечатаны и записаны на диск.

Когда определены несколько общих блоков, они обрабатываются по порядку. В вашем случае 3 это похоже на запуск плагина дважды. Но обратите внимание, что у них CommonsChunkPluginможет быть более сложная конфигурация (minSize, minChunks и т. Д.), Которая влияет на способ перемещения модулей.

СЛУЧАЙ 1:

  1. Есть 3 entryблока ( entry1, entry2и vendors).
  2. Конфигурация устанавливает commonsчанк как общий.
  3. Плагин обрабатывает commons общий чанк (поскольку чанк не существует, он создается):
    1. Он собирает модули, которые используются более чем один раз в других кусках: entry1, entry2и vendorsиспользовать jqueryтаким образом , модуль удаляется из этих кусков и добавляется в commonsблок.
    2. commonsКусок помечен как entryкусок в то время как entry1, entry2и vendorsкуски , как unflagged entry.
  4. Наконец, поскольку commonsфрагмент является entryфрагментом, он содержит среду выполнения и jqueryмодуль.

СЛУЧАЙ 2:

  1. Есть 3 entryблока ( entry1, entry2и vendors).
  2. Конфигурация устанавливает vendorsчанк как общий.
  3. Плагин обрабатывает vendorsобщий чанк:
    1. Он собирает модули, которые используются более одного раза в других фрагментах: entry1и entry2использует, jqueryчтобы модуль был удален из этих фрагментов (обратите внимание, что он не добавляется в vendorsфрагмент, потому чтоvendors чанк уже содержит его).
    2. vendorsКусок помечен как entryкусок в то время как entry1и entry2куски , как unflagged entry.
  4. Наконец, поскольку vendorsблок является entryблоком, он содержит среду выполнения и jquery/ jquery_pluginmodules.

СЛУЧАЙ 3:

  1. Есть 3 entryблока ( entry1, entry2и vendors).
  2. Конфигурация устанавливает vendorsфрагмент и manifestфрагмент как общие фрагменты.
  3. Плагин создает manifestчанк, поскольку он не существует.
  4. Плагин обрабатывает vendorsобщий чанк:
    1. Он собирает модули, которые используются более одного раза в других чанках: entry1и entry2использует jqueryтак, чтобы модуль был удален из этих чанков (обратите внимание, что он не добавляется в vendorsчанк, потому чтоvendors чанк уже содержит его).
    2. vendorsКусок помечен как entryкусок в то время как entry1и entry2куски , как unflagged entry.
  5. Плагин обрабатываетmanifest общий чанк (поскольку чанк не существует, он создается):
    1. Он собирает модули, которые используются более одного раза в других чанках: поскольку нет модулей, используемых более одного раза, ни один модуль не перемещается.
    2. manifestКусок помечен как entryкусок в то время entry1, entry2и vendorsявляются unflagged , как entry.
  6. Наконец, поскольку manifestфрагмент является entryфрагментом, он содержит среду выполнения.

Надеюсь, поможет.

Лоран Этиембл
источник
Несколько вещей, которые я хочу спросить / уточнить, пожалуйста, добавьте эти моменты к своему ответу: 1) Можете ли вы сделать то же пошаговое объяснение для СЛУЧАЯ 1, только чтобы ответ был полным на 1000%? 2) Запуск плагина с помощью { names : ['vendors', 'manifest'] }похож на запуск его дважды, один раз с { name : 'vendors' }и один раз с { name : 'manifest' }, правильно? 3) Когда мы говорим «Плагин обрабатывает общий фрагмент», мы имеем в виду, что он создает содержимое, которое будет вставлять в bundle.jsфайл, в памяти, правильно? 4) Пока он не «обработал все общие куски», он вообще не записал никаких выходных данных в файл, он все в памяти
Димитрис Карагианнис
2
У меня есть еще один вопрос, если вы хотите ответить. Скажем, что в моем примере сверху, entry1.jsи entry2.jsмежду ними был другой общий файл, кроме jqueryфайла, назовем его ownLib.js. В СЛУЧАЕ 2 и СЛУЧАЕ 3 ownLib.jsокажется ли в vendors.bundle.jsправильном? Как бы вы сделали так, чтобы общие файлы, отличные от файлов поставщиков, были разделены на отдельные фрагменты, кроме vendorsфрагментов? Извините за беспокойство, но я все еще изучаю, как использовать webpack
Димитрис
7
Да, правильно: ownLib.jsбудет помещен в первый общий блок. Если вы хотите , чтобы собрать общие зависимости в другом chunck, вы должны пройти что - то вроде этого: { names : ['common', 'vendors', 'manifest'] }.
Laurent Etiemble
6
Отличный вопрос, отличный ответ, отличное обсуждение. Кажется, я наконец понял.
oluckyman
3
Я провел последний день за чтением документации CommonsChunkPlugin, и это первое место, где я прочитал, что после выполнения обработанные фрагменты «не помечаются как запись». Это в основном объясняет все, с чем у меня были проблемы - если бы я мог проголосовать больше одного раза, я бы это сделал.
Coderer