Aprende Webpack desde cero

Webpack caching, hashes & substitutions

  • Cuando publicamos nuestro directorio /dist (o como lo hayas llamado) al servidor, podemos aplicar técnicas de caché para que evitar requests innecesarios en casa de que haya módulos de nuestra aplicación que no hayan cambiado.
  • Antes utilizaban cambios en la URL de cada dependencia, pero aunque el tema de caché y sus técnicas pueden llegar a ser bastante complicadas, nosotros tenemos suerte: Webpack dispone de una serie de mecanismos out of the box que permiten implementarlas de forma sencilla.
  • Me estoy refieriendo a las sustituciones de Webpack. Durante el curso ya hemos utilizado algunas. De hecho en nuestra configuración usamos [name] y [hash]. La primera no tiene nada que ver con el caché y es sólo para ubicar mejor la pertenencia de cada módulo. La segunda es más interesante ya que se calcula con el identificador de cada módulo.
  • Pero, ¿qué son las sustituciones?. Son el método que tiene Webpack para alterar nombres de archivos teniendo en cuenta algunas condiciones. En esencia tenemos [hash], [chunkhash] y [contenthash]. Veamos las diferencias.
  • Si dejamos todo como está (es decir, usando [hash]) y lanzamos una tarea build, el resultado arroja Hash: 67a298514ec96f3c6283 y todos los archivos contiene el mismo hash. Esto es porque se genera en base a la compilación. Si no cambio nada y vuelvo a compilar debería tener el mismo valor.
  • No es posible crear una estrategia de cacheado a largo plazo si con cada nuevo build con cambios potenciales cambiamos el hash.
  • Por otro lado [chunkhash] se basa en el contenido de cada chunk que compone un módulo. Por lo que cada chunk tiene un valor diferente. Sólo si cambia alguna de las dependencias de un chunk cambiará el valor, con la ventaja de que el resto de chunks seguirán igual.
  • Hagamos una prueba configurando MiniCSSExtractPlugin para que haga uso de esta sustitución y lanzar un build.
plugins: [
  ...
  new MiniCSSExtractPlugin({
    filename: "[name].[chunkhash].css"
  }),
  ...
],
  • De esta forma, cada chunk tiene un valor de hash diferente. Si sólo cambiamos uno de ellos (o sus dependencias, porque pueden haber varios miembros del mismo chunk con formato de matriz) es cuando el valor cambia.
main.1547dae1d7d26ff174ce.bundle.js
main.1547dae1d7d26ff174ce.css
  • Fíjate que el valor de hash para los miembros del mismo chunk es el mismo, aunque sólo uno de ellos cambie.
  • Por último tenemos [contenthash] el cual es tremendamente útil. Esta sustitución genera un hash basado en el contenido de un recurso, por lo que cada archivo individual tendrá un hash diferente. Si este cambia, también cambia el hash. De igual modo, si no ha cambiado el mismo hash permance.
main.c65ff8c778569828ba3c.bundle.js
main.d33b00453f58bc6ffb94.css
  • Antes de continuar debes saber que salvo [hash] no se pueden utilizar sustituciones en modo desarrollo. Si intentamos lanzar un serve Webpack nos responderá con Cannot use [chunkhash] or [contenthash] for chunk in '[name].[contenthash].bundle.js' (use [hash] instead).
  • Aunque más adelante compondremos una configuración diferenciada, por ahora podemos solucionarlo usando nuestra constante isDevelopment.
filename: `[name].[${isDevelopment ? "hash" : "contenthash"}].bundle.js`
optimization: {
  runtimeChunk: "single",
},
  • Ahora podemos ver como el runtime tiene su propio chunk con el hash adecuado.
runtime.1ec45c75f8920b30b5ec.bundle.js
  • Podemos más allá y extraer también dependencias externas, como Vue en nuestro caso, creando un cacheGroup con todo lo que se añada al gráfico de dependencias y que proceda de node_modules.
optimization: { ---
  runtimeChunk: "single",
  splitChunks: {
    cacheGroups: {
      vendor: {
        test: /[\\/]node_modules[\\/]/,
        name: "vendors",
        chunks: "all",
      },
    },
  },
},
  • Obteniendo nuestro chunk vendor.
vendors.490e237c8f0dd782e0f8.bundle.js
  • El plugin SplitChunksPlugin es super poderoso y usándolo correctamente puede mejorar la experiencia de uso de tu apliación. Tiene varias opciones. Por ejemplo para dividir en trozos (chunks) según un tamaño. Si qusiéramos dividir los los chunks que superen los 10kb (algo bastante irreal).
optimization: {
    ...
  splitChunks: {
    maxSize: 10000,
    ...
  },
},
  • Este tipo de opciones pueden ayudar reducir el tamaño de algunos chunks y por lo tanto permitir que algunos se descarguen en paralelo, haciendo que tu App sea funcional antes.

No te pierdas ninguna novedad

Escuela Vue en Twitter

Participa en la Comunidad Escuela Vue

Comunidad Escuela Vue