¿Alguien puede explicar CommonsChunkPlugin de Webpack?

82

Entiendo la esencia general de que CommonsChunkPluginexamina todos los puntos de entrada, comprueba si hay paquetes / dependencias comunes entre ellos y los separa en su propio paquete.

Entonces, supongamos que tengo la siguiente configuración:

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

Si empaqueto sin usar CommonsChunkPlugin

Terminaré con 3 nuevos archivos de paquete:

  • entry1.bundle.jsel cual contiene el código completo de entry1.jsy jqueryy contiene su propio tiempo de ejecución
  • entry2.bundle.jsel cual contiene el código completo de entry2.jsy jqueryy contiene su propio tiempo de ejecución
  • vendors.bundle.jsel cual contiene el código completo de jqueryy some_jquery_pluginy contiene su propio tiempo de ejecución

Obviamente, esto es malo porque potencialmente cargaré jquery3 veces en la página, por lo que no queremos eso.

Si empaqueto usando CommonsChunkPlugin

Dependiendo de los argumentos que pase a CommonsChunkPlugincualquiera de los siguientes sucederá:

  • CASO 1: Si apruebo { name : 'commons' }, terminaré con los siguientes archivos de paquete:

    • entry1.bundle.jsque contiene el código completo de entry1.js, un requisito para jqueryy no contiene el tiempo de ejecución
    • entry2.bundle.jsque contiene el código completo de entry2.js, un requisito para jqueryy no contiene el tiempo de ejecución
    • vendors.bundle.jsque contiene el código completo de some_jquery_plugin, un requisito para jqueryy no contiene el tiempo de ejecución
    • commons.bundle.jsque contiene el código completo jqueryy contiene el tiempo de ejecución

    De esta manera, terminamos con algunos paquetes más pequeños en general y el tiempo de ejecución está contenido en el commonspaquete. Bastante bien, pero no ideal.

  • CASO 2: Si apruebo { name : 'vendors' }, terminaré con los siguientes archivos de paquete:

    • entry1.bundle.jsque contiene el código completo de entry1.js, un requisito para jqueryy no contiene el tiempo de ejecución
    • entry2.bundle.jsque contiene el código completo de entry2.js, un requisito para jqueryy no contiene el tiempo de ejecución
    • vendors.bundle.jsque contiene el código completo de jqueryy some_jquery_pluginy contiene el tiempo de ejecución.

    De esta manera, nuevamente, terminamos con algunos paquetes más pequeños en general, pero el tiempo de ejecución ahora está contenido en el vendorspaquete. Es un poco peor que el caso anterior, ya que el tiempo de ejecución ahora está en el vendorspaquete.

  • CASO 3: Si apruebo { names : ['vendors', 'manifest'] }, terminaré con los siguientes archivos de paquete:

    • entry1.bundle.jsque contiene el código completo de entry1.js, un requisito para jqueryy no contiene el tiempo de ejecución
    • entry2.bundle.jsque contiene el código completo de entry2.js, un requisito para jqueryy no contiene el tiempo de ejecución
    • vendors.bundle.jsque contiene el código completo de jqueryy some_jquery_pluginno contiene el tiempo de ejecución
    • manifest.bundle.js que contiene requisitos para todos los demás paquetes y contiene el tiempo de ejecución

    De esta manera, terminamos con algunos paquetes más pequeños en general y el tiempo de ejecución está contenido en el manifestpaquete. Este es el caso ideal.

Lo que no entiendo / no estoy seguro de entender

  • En el CASO 2, ¿por qué terminamos con el vendorspaquete que contiene tanto el código común ( jquery) como lo que queda de la vendorsentrada ( some_jquery_plugin)? Según tengo entendido, lo que CommonsChunkPluginhizo aquí fue que reunió el código común ( jquery), y dado que lo obligamos a vendorsenviarlo al paquete, "fusionó" el código común en el vendorspaquete (que ahora solo contenía el código de some_jquery_plugin). Por favor confirme o explique.

  • En el CASO 3 no entiendo qué pasó cuando pasamos { names : ['vendors', 'manifest'] }al plugin. ¿Por qué / cómo se vendorsmantuvo intacto el paquete, que contiene ambos jqueryy some_jquery_plugin, cuándo jqueryes claramente una dependencia común, y por qué se manifest.bundle.jscreó el archivo generado de la forma en que se creó (requiriendo todos los demás paquetes y conteniendo el tiempo de ejecución)?

Dimitris Karagiannis
fuente
Para el caso 1, creo que debería especificar la propiedad minChunks.
Marko
10
He aprendido mucho de tu pregunta , ¡muchas gracias!
Rafael Eyng
1
Muchas gracias por preguntar esto y aclarar mis confusiones todo este tiempo en este complemento ❤
Glenn Mohammad
Tal vez alguien sepa, ¿cómo se verán estos ejemplos en Webpack 4?
StalkAlex

Respuestas:

105

Así es como el CommonsChunkPlugin funciona.

Un fragmento común "recibe" los módulos compartidos por varios fragmentos de entrada. Un buen ejemplo de una configuración compleja se puede encontrar en el repositorio de Webpack .

Se CommonsChunkPluginejecuta durante la fase de optimización de Webpack, lo que significa que opera en la memoria, justo antes de que los fragmentos se sellen y escriban en el disco.

Cuando se definen varios fragmentos comunes, se procesan en orden. En su caso 3, es como ejecutar el complemento dos veces. Pero tenga en cuenta que CommonsChunkPluginpuede tener una configuración más compleja (minSize, minChunks, etc.) que afecta la forma en que se mueven los módulos.

CASO 1:

  1. Hay 3 entryfragmentos ( entry1, entry2y vendors).
  2. La configuración establece el commonsfragmento como un fragmento común.
  3. El complemento procesa el commons fragmento común (dado que el fragmento no existe, se crea):
    1. Recoge los módulos que se utilizan más de una vez en los otros trozos: entry1, entry2y vendorsuso jqueryde modo que el módulo se retira de estos trozos y se añade a la commonsporción.
    2. El commonsfragmento se marca como un entryfragmento, mientras que los fragmentos entry1, entry2y no vendorsse marcan como entry.
  4. Finalmente, dado que el commonsfragmento es un entryfragmento, contiene el tiempo de ejecución y el jquerymódulo.

CASO 2:

  1. Hay 3 entryfragmentos ( entry1, entry2y vendors).
  2. La configuración establece el vendorsfragmento como un fragmento común.
  3. El complemento procesa el vendorsfragmento común:
    1. Recopila los módulos que se usan más de una vez en los otros fragmentos: entry1y entry2usa jquerypara que el módulo se elimine de estos fragmentos (tenga en cuenta que no se agrega al vendorsfragmentovendors fragmento ya lo contiene).
    2. El vendorsfragmento se marca como un entryfragmento mientras que los fragmentos entry1y no entry2se marcan como entry.
  4. Finalmente, dado que el vendorsfragmento es un entryfragmento, contiene el tiempo de ejecución y los módulos jquery/ jquery_plugin.

CASO 3:

  1. Hay 3 entryfragmentos ( entry1, entry2y vendors).
  2. La configuración establece el vendorsfragmento y el manifestfragmento como fragmentos comunes.
  3. El complemento crea el manifestfragmento ya que no existe.
  4. El complemento procesa el vendorsfragmento común:
    1. Recopila los módulos que se usan más de una vez en los otros fragmentos: entry1y entry2use jquerypara que el módulo se elimine de estos fragmentos (tenga en cuenta que no se agrega al vendorsfragmento porque elvendors fragmento ya lo contiene).
    2. El vendorsfragmento se marca como un entryfragmento mientras que los fragmentos entry1y no entry2se marcan como entry.
  5. El complemento procesa elmanifest fragmento común (dado que el fragmento no existe, se crea):
    1. Recopila los módulos que se utilizan más de una vez en los otros fragmentos: como no hay módulos utilizados más de una vez, no se mueve ningún módulo.
    2. El manifestfragmento se marca como entryfragmento mientras que entry1, entry2y no vendorsse marcan como entry.
  6. Finalmente, dado que el manifestfragmento es un entryfragmento, contiene el tiempo de ejecución.

Espero eso ayude.

Laurent Etiemble
fuente
Algunas cosas que quiero preguntar / aclarar, agregue estos puntos a su respuesta también: 1) ¿Puede hacer la misma explicación paso a paso para el CASO 1 solo para que la respuesta esté completa al 1000%? 2) Ejecutar el complemento con { names : ['vendors', 'manifest'] }es como ejecutarlo dos veces, una vez con { name : 'vendors' }y una vez con { name : 'manifest' }, ¿correcto? 3) Cuando decimos "El complemento procesa un fragmento común" queremos decir que crea el contenido que escupirá en el bundle.jsarchivo, en la memoria, ¿correcto? 4) Hasta que se "procesa todos los fragmentos comunes", no se ha escrito ninguna salida en un archivo, todo está en la memoria
Dimitris Karagiannis
2
Tengo una pregunta más, si tiene ganas de responder. Digamos que en mi ejemplo anterior, entry1.jsy entry2.jsteníamos otro archivo común entre ellos, además del jqueryarchivo, llamémoslo ownLib.js. En el CASO 2 y el CASO 3, ownLib.js¿terminaría en el vendors.bundle.jscorrecto? ¿Cómo haría para que los archivos comunes que no sean archivos de proveedores se separen en su propio fragmento, aparte del vendorsfragmento? Perdón por molestarte, pero todavía estoy aprendiendo a usar el paquete web
Dimitris Karagiannis
7
Sí, eso es correcto: ownLib.jsse colocaría en el primer trozo común. Si se quiere recoger las dependencias comunes en otro chunck, usted tiene que pasar algo como esto: { names : ['common', 'vendors', 'manifest'] }.
Laurent Etiemble
6
Gran pregunta, gran respuesta, gran discusión. Parece que finalmente lo entiendo.
oluckyman
3
Pasé el último día leyendo documentos de CommonsChunkPlugin y este es el primer lugar donde leí que, después de ejecutar, los fragmentos procesados ​​"no están marcados como entrada". Eso básicamente explica todo con lo que estaba teniendo problemas: si pudiera votar a favor más de una vez, lo haría.
Coderer