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

Я понимаю общий смысл, что 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 which contains the complete code from entry1.js, a requirement for jquery and does not contain the runtime
    • entry2.bundle.js, который содержит полный код из entry2.js, требование для jquery и не содержит среды выполнения
    • vendors.bundle.js, который содержит полный код из some_jquery_plugin, требование для jquery и не содержит среды выполнения
    • commons.bundle.js, который содержит полный код из jquery и содержит среду выполнения

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

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

    • entry1.bundle.js which contains the complete code from entry1.js, a requirement for jquery and does not contain the runtime
    • entry2.bundle.js, который содержит полный код из entry2.js, является требованием для jquery и не содержит среды выполнения.
    • vendors.bundle.js, который содержит полный код из jquery и some_jquery_plugin и содержит среду выполнения.

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

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

    • entry1.bundle.js which contains the complete code from entry1.js, a requirement for jquery and does not contain the runtime
    • entry2.bundle.js, который содержит полный код из entry2.js, требование для jquery и не содержит среды выполнения
    • vendors.bundle.js, который содержит полный код из jquery и some_jquery_plugin и не содержит среды выполнения
    • manifest.bundle.js, который содержит требования для всех остальных пакетов и содержит среду выполнения

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

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

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

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


person Dimitris Karagiannis    schedule 17.09.2016    source источник
comment
Для случая 1, я думаю, вы должны указать свойство minChunks.   -  person Marko    schedule 19.09.2016
comment
Я многому научился из вашего вопроса, большое спасибо!   -  person Rafael Eyng    schedule 31.05.2017
comment
Большое спасибо, что спросили об этом и прояснили мои недоразумения все это время с этим плагином ❤   -  person Glenn Mohammad    schedule 29.10.2017
comment
Может кто знает, как эти примеры будут выглядеть в Webpack 4?   -  person StalkAlex    schedule 08.03.2018


Ответы (1)


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

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

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

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

СЛУЧАЙ 1:

  1. Есть 3 фрагмента entry (entry1, entry2 и vendors).
  2. Конфигурация устанавливает чанк commons как общий чанк.
  3. The plugin processes the commons common chunk (since the chunk does not exist, it is created):
    1. It collects the modules that are used more than once in the other chunks: entry1, entry2 and vendors use jquery so the module is removed from these chunks and is added to the commons chunk.
    2. Фрагмент commons помечается как фрагмент entry, а фрагменты entry1, entry2 и vendors не помечаются как entry.
  4. Наконец, поскольку фрагмент commons является фрагментом entry, он содержит среду выполнения и модуль jquery.

СЛУЧАЙ 2:

  1. Есть 3 фрагмента entry (entry1, entry2 и vendors).
  2. Конфигурация устанавливает чанк vendors как общий чанк.
  3. The plugin processes the vendors common chunk:
    1. It collects the modules that are used more than once in the other chunks: entry1 and entry2 use jquery so the module is removed from these chunks (note that it is not added to the vendors chunk because the vendors chunk already contains it).
    2. Фрагмент vendors помечается как фрагмент entry, а фрагменты entry1 и entry2 не помечаются как entry.
  4. Наконец, поскольку фрагмент vendors является фрагментом entry, он содержит среду выполнения и модули jquery/jquery_plugin.

СЛУЧАЙ 3:

  1. Есть 3 фрагмента entry (entry1, entry2 и vendors).
  2. Конфигурация устанавливает фрагмент vendors и фрагмент manifest как общие фрагменты.
  3. Плагин создает фрагмент manifest, так как он не существует.
  4. The plugin processes the vendors common chunk:
    1. It collects the modules that are used more than once in the other chunks: entry1 and entry2 use jquery so the module is removed from these chunks (note that it is not added to the vendors chunk because the vendors chunk already contains it).
    2. Фрагмент vendors помечается как фрагмент entry, а фрагменты entry1 и entry2 не помечаются как entry.
  5. The plugin processes the manifest common chunk (since the chunk does not exist, it is created):
    1. It collects the modules that are used more than once in the other chunks: as there are no modules used more than once, no module is moved.
    2. Фрагмент manifest помечен как фрагмент entry, а фрагменты entry1, entry2 и vendors не помечены как entry.
  6. Наконец, поскольку фрагмент manifest является фрагментом entry, он содержит среду выполнения.

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

person Laurent Etiemble    schedule 20.09.2016
comment
Несколько вещей, которые я хочу спросить / уточнить, пожалуйста, добавьте эти пункты и к вашему ответу: 1) Можете ли вы сделать такое же пошаговое объяснение для СЛУЧАЯ 1, чтобы ответ был на 1000% полным? 2) Запуск плагина с { names : ['vendors', 'manifest'] } похож на запуск его дважды, один раз с { name : 'vendors' } и один раз с { name : 'manifest' }, правильно? 3) Когда мы говорим, что плагин обрабатывает общий фрагмент, мы имеем в виду, что он создает содержимое, которое будет выплевывать в файл bundle.js в памяти, верно? 4) Пока он не обработает все общие фрагменты, он вообще не записывает вывод в файл, все находится в памяти. - person Dimitris Karagiannis; 21.09.2016
comment
5) Учитывая 2) выше и ваше объяснение. Он собирает модули, которые используются более 1 раза в других чанках: поскольку нет модулей, используемых более 1 раза, ни один модуль не перемещается. это означает, что второй вызов плагина работает с результатами в памяти первого вызова плагина (модуль jquery уже удален из других фрагментов, поэтому он просто находит один экземпляр, где существует полный код jquery, то есть содержимое чанка vendors), верно? 6) куски == в памяти, пакеты == на выходе, верно? - person Dimitris Karagiannis; 21.09.2016
comment
7) плагин, назначающий последний общий фрагмент как фрагмент entry вместо фрагмента normal, я полагаю, является поведением по умолчанию, верно? Большое спасибо за ваш ответ - person Dimitris Karagiannis; 21.09.2016
comment
(на самом деле игнорируйте 7) :) ) - person Dimitris Karagiannis; 21.09.2016
comment
У меня есть еще один вопрос, если вы хотите ответить. Предположим, что в моем примере сверху entry1.js и entry2.js между ними был еще один общий файл, отличный от файла jquery, назовем его ownLib.js. В СЛУЧАЕ 2 и СЛУЧАЕ 3 ownLib.js окажется в vendors.bundle.js правильно? Как бы вы сделали так, чтобы общие файлы, отличные от файлов поставщиков, были разделены на отдельный фрагмент, кроме фрагмента vendors? Извините за беспокойство, но я все еще учусь использовать веб-пакет - person Dimitris Karagiannis; 28.09.2016
comment
Да, верно: ownLib.js будет помещен в первый общий фрагмент. Если вы хотите собрать общие зависимости в другом фрагменте, вам нужно передать что-то вроде этого: { names : ['common', 'vendors', 'manifest'] }. - person Laurent Etiemble; 29.09.2016
comment
Отличный вопрос, отличный ответ, отличная дискуссия. Кажется, я понял наконец. - person oluckyman; 04.10.2016
comment
@LaurentEtiemble: новый webpack.optimize.CommonsChunkPlugin({names: ['common', 'vendor', 'manifest'], чанки: ['main', 'Home'], }), это моя конфигурация, но среда выполнения webpack присутствует во всех общих фрагментах. Можете ли вы объяснить, почему? - person Bhargavi Gunda; 21.11.2016
comment
@BhargaviGunda Я предполагаю, что это как-то связано с тем, что вы говорите ему запускать плагин только на кусках main и Home, а не на всем? - person Dimitris Karagiannis; 20.01.2017
comment
Я провел последний день, читая документацию CommonsChunkPlugin, и это первое место, где я прочитал, что после выполнения обработанные фрагменты не помечаются как запись. Это в основном объясняет все, с чем у меня были проблемы - если бы я мог проголосовать более одного раза, я бы это сделал. - person Coderer; 29.09.2017
comment
Что было бы действительно полезно для этого ответа, так это окончательная конфигурация, которая достигает того, к чему стремился ОП. В вопросе и ответе упоминается так много вариантов, что очень сложно понять, как выглядит фактический результат! - person toxaq; 15.02.2018
comment
Спасибо за отличное объяснение. Может быть, кто-нибудь знает, как перенести эти 3 варианта в Webpack 4? - person StalkAlex; 08.03.2018
comment
Вы можете это объяснить? «Наконец, поскольку часть общих ресурсов является частью входа, она содержит среду выполнения и модуль jquery». - person Suraj Jain; 23.04.2019