Czy ktoś może wyjaśnić CommonsChunkPlugin Webpack

82

Rozumiem, że patrzę CommonsChunkPluginna wszystkie punkty wejścia, sprawdza, czy istnieją między nimi wspólne pakiety / zależności i rozdziela je do własnego pakietu.

Załóżmy więc, że mam następującą konfigurację:

...
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'
}
...

Jeśli spakuję bez użycia CommonsChunkPlugin

Skończy się na 3 nowych plikach pakietu:

  • entry1.bundle.jsktóra zawiera kompletny kod entry1.jsi jqueryi zawiera swój własny czas pracy
  • entry2.bundle.jsktóra zawiera kompletny kod entry2.jsi jqueryi zawiera swój własny czas pracy
  • vendors.bundle.jsktóra zawiera kompletny kod jqueryi some_jquery_plugini zawiera swój własny czas pracy

Jest to oczywiście złe, ponieważ potencjalnie załaduję stronę jquery3 razy, więc tego nie chcemy.

Jeśli połączę za pomocą CommonsChunkPlugin

W zależności od tego, jakie argumenty przekażę do CommonsChunkPluginktóregokolwiek z poniższych:

  • PRZYPADEK 1: Jeśli zdam { name : 'commons' }, skończę z następującymi plikami w pakiecie:

    • entry1.bundle.jsktóry zawiera pełny kod z entry1.js, wymóg jqueryi nie zawiera środowiska uruchomieniowego
    • entry2.bundle.jsktóry zawiera pełny kod z entry2.js, wymóg jqueryi nie zawiera środowiska uruchomieniowego
    • vendors.bundle.jsktóry zawiera pełny kod z some_jquery_plugin, wymóg jqueryi nie zawiera środowiska uruchomieniowego
    • commons.bundle.jsktóry zawiera pełny kod z jqueryi zawiera środowisko wykonawcze

    W ten sposób otrzymujemy ogólnie kilka mniejszych pakietów, a środowisko wykonawcze jest zawarte w commonspakiecie. Całkiem ok, ale nie idealne.

  • PRZYPADEK 2: Jeśli zdam { name : 'vendors' }, otrzymam następujące pliki pakietu:

    • entry1.bundle.jsktóry zawiera pełny kod z entry1.js, wymóg jqueryi nie zawiera środowiska uruchomieniowego
    • entry2.bundle.jsktóry zawiera pełny kod z entry2.js, wymóg jqueryi nie zawiera środowiska uruchomieniowego
    • vendors.bundle.jsktóra zawiera kompletny kod jqueryi some_jquery_pluginoraz zawiera środowisko wykonawcze.

    W ten sposób ponownie otrzymujemy ogólnie kilka mniejszych pakietów, ale środowisko wykonawcze jest teraz zawarte w vendorspakiecie. Jest trochę gorzej niż w poprzednim przypadku, ponieważ środowisko wykonawcze znajduje się teraz w vendorspakiecie.

  • PRZYPADEK 3: Jeśli zdam { names : ['vendors', 'manifest'] }, otrzymam następujące pliki pakietu:

    • entry1.bundle.jsktóry zawiera pełny kod z entry1.js, wymóg jqueryi nie zawiera środowiska uruchomieniowego
    • entry2.bundle.jsktóry zawiera pełny kod z entry2.js, wymóg jqueryi nie zawiera środowiska uruchomieniowego
    • vendors.bundle.jsktóry zawiera pełny kod zi jqueryi some_jquery_pluginnie zawiera środowiska wykonawczego
    • manifest.bundle.js który zawiera wymagania dla każdego innego pakietu i zawiera środowisko wykonawcze

    W ten sposób otrzymujemy ogólnie kilka mniejszych pakietów, a środowisko wykonawcze jest zawarte w manifestpakiecie. To jest idealny przypadek.

Czego nie rozumiem / nie jestem pewien, czy rozumiem

  • W przypadku 2 Dlaczego skończyć z vendorspakietu zawierającego zarówno wspólny kod ( jquery) i co pozostało z vendorswejściem ( some_jquery_plugin)? Z mojego zrozumienia CommonsChunkPluginwynika, że ​​w tym przypadku zebrał wspólny kod ( jquery), a ponieważ zmusiliśmy go do wyprowadzenia go do vendorspakietu, w pewnym sensie „scalił” wspólny kod z vendorspakietem (który teraz zawierał tylko kod z some_jquery_plugin). Potwierdź lub wyjaśnij.

  • W PRZYPADKU 3 nie rozumiem, co się stało, gdy przeszliśmy { names : ['vendors', 'manifest'] }do wtyczki. Dlaczego / w jaki sposób vendorspakiet został zachowany w stanie nienaruszonym, zawierający obie, jquerya some_jquery_pluginkiedy jqueryjest wyraźnie wspólną zależnością i dlaczego wygenerowany manifest.bundle.jsplik został utworzony w taki sposób, w jaki został utworzony (wymagający wszystkich innych pakietów i zawierający środowisko wykonawcze)?

Dimitris Karagiannis
źródło
W przypadku 1 myślę, że powinieneś określić właściwość minChunks.
Marko,
10
Wiele się nauczyłem z twojego pytania , wielkie dzięki!
Rafael Eyng
1
Dziękuję bardzo za pytanie i wyjaśnienie moich nieporozumień przez cały czas tej wtyczki ❤
Glenn Mohammad
Może ktoś wie, jak ten przykład będzie wyglądał w Webpack 4?
StalkAlex

Odpowiedzi:

105

Tak to CommonsChunkPlugindziała.

Wspólny fragment „otrzymuje” moduły współdzielone przez kilka fragmentów wejściowych. Dobry przykład złożonej konfiguracji można znaleźć w repozytorium Webpack .

Plik CommonsChunkPluginProwadzony jest w fazie optymalizacji Webpack, co oznacza, że działa w pamięci, tuż przed kawałki są uszczelnione i zapisane na dysku.

Gdy zdefiniowano kilka typowych fragmentów, są one przetwarzane w kolejności. W twoim przypadku 3 jest to jak dwukrotne uruchomienie wtyczki. Należy jednak pamiętać, że CommonsChunkPluginmoże mieć bardziej złożoną konfigurację (minSize, minChunks itp.), Która wpływa na sposób przenoszenia modułów.

PRZYPADEK 1:

  1. Istnieją 3 entrykawałki ( entry1, entry2ivendors ).
  2. Konfiguracja ustawia commons porcję jako wspólną porcję.
  3. Wtyczka przetwarza plik commons wspólny fragment (ponieważ fragment nie istnieje, jest tworzony):
    1. Zbiera się moduły, które są używane więcej niż raz w innych fragmentach: entry1, entry2i vendorsstosowanie jquerytak moduł usuniętych z tych kawałków jest dodawane do commonsfragmentu.
    2. Fragment commonsjest oznaczony jako entryfragment, podczas gdy fragmenty entry1, entry2a nie vendorssą oznaczone jako entry.
  4. Wreszcie, ponieważ commonsfragment jest entryfragmentem, zawiera on środowisko wykonawcze i jquerymoduł.

PRZYPADEK 2:

  1. Istnieją 3 entrykawałki ( entry1, entry2ivendors ).
  2. Konfiguracja ustawiavendors porcję jako wspólną porcję.
  3. Wtyczka przetwarza vendorswspólny fragment:
    1. Zbiera moduły, które są używane więcej niż raz w innych porcjach: entry1i entry2użyj, jqueryaby moduł został usunięty z tych fragmentów (pamiętaj, że nie jest dodawany do vendorsfragmentu, ponieważvendors fragment już go zawiera).
    2. Fragment vendorsjest oznaczony jako entryfragment, podczas gdy fragmenty entry1i nie entry2są oznaczone jako entry.
  4. Wreszcie, ponieważ vendorsfragment jest entryfragmentem, zawiera on środowisko wykonawcze i jquery/ jquery_pluginmoduły.

PRZYPADEK 3:

  1. Istnieją 3 entrykawałki ( entry1, entry2ivendors ).
  2. Konfiguracja ustawia vendorsfragment i manifestfragment jako wspólne fragmenty.
  3. Wtyczka tworzy manifestfragment, ponieważ nie istnieje.
  4. Wtyczka przetwarza vendorswspólny fragment:
    1. Zbiera moduły, które są używane więcej niż raz w innych porcjach: entry1i entry2użyj, jqueryaby moduł został usunięty z tych fragmentów (pamiętaj, że nie jest dodawany do vendorsfragmentu, ponieważvendors fragment już go zawiera).
    2. Fragment vendorsjest oznaczony jako entryfragment, podczas gdy fragmenty entry1i nie entry2są oznaczone jako entry.
  5. Wtyczka przetwarzamanifest wspólny fragment (ponieważ fragment nie istnieje, jest tworzony):
    1. Zbiera moduły, które są używane więcej niż raz w innych fragmentach: ponieważ nie ma modułów używanych więcej niż raz, żaden moduł nie jest przenoszony.
    2. Fragment manifestjest oflagowany jako entryfragment entry1, entry2a nie vendorsjest oznaczony jako entry.
  6. Wreszcie, ponieważ manifestfragment jest entryfragmentem, zawiera on środowisko wykonawcze.

Mam nadzieję, że to pomoże.

Laurent Etiemble
źródło
Kilka rzeczy, o które chcę zapytać / wyjaśnić, proszę również dodać te punkty do swojej odpowiedzi: 1) Czy możesz zrobić to samo wyjaśnienie krok po kroku dla PRZYPADKU 1, aby odpowiedź była w 1000% kompletna? 2) Uruchomienie wtyczki za pomocą { names : ['vendors', 'manifest'] }jest jak uruchomienie jej dwa razy, raz z { name : 'vendors' }i raz z { name : 'manifest' }, prawda? 3) Kiedy mówimy „Wtyczka przetwarza wspólny fragment”, mamy na myśli to, że tworzy zawartość, którą wypluje w bundle.jspliku, w pamięci, prawda? 4) Dopóki nie "przetworzy wszystkich typowych fragmentów", nie zapisał żadnych danych wyjściowych do pliku, wszystko jest w pamięci
Dimitris Karagiannis
2
Mam jeszcze jedno pytanie, jeśli zechcesz odpowiedzieć. Powiedzmy, że w moim przykładzie z góry entry1.jsi entry2.jsmiędzy nimi był inny wspólny plik, inny niż jqueryplik, nazwijmy to ownLib.js. Czy w PRZYPADKU 2 i PRZYPADKU 3 ownLib.jsskończyłoby się to vendors.bundle.jspoprawnie? Jak można to zrobić, aby wspólne pliki, inne niż pliki dostawców, były rozdzielane na osobne fragmenty, poza vendorsfragmentem? Przepraszam, że przeszkadzam, ale wciąż uczę się korzystać z webpacka
Dimitris Karagiannis
7
Tak, zgadza się: ownLib.jszostanie umieszczony w pierwszym wspólnym kawałku. Jeśli chcesz zbierać wspólnych zależności, w innym chunck, trzeba przejść coś takiego: { names : ['common', 'vendors', 'manifest'] }.
Laurent Etiemble
6
Świetne pytanie, świetna odpowiedź, świetna dyskusja. Wygląda na to, że w końcu to rozumiem.
oluckyman
3
Spędziłem ostatni dzień na czytaniu dokumentacji CommonsChunkPlugin i jest to pierwsze miejsce, w którym przeczytałem, że po wykonaniu przetworzone fragmenty „są nieoflagowane jako wpis”. To w zasadzie wyjaśnia wszystko, z czym miałem problem - gdybym mógł zagłosować za więcej niż raz, zrobiłbym to.
Coderer