Skip to main content




Una nueva estrategia de fragmentación de paquetes web en Next.js y Gatsby minimiza el código duplicado para impulsar el rendimiento de carga de la página.

Chrome está colaborando con herramientas y marcos en el ecosistema de código abierto de JavaScript. Recientemente se agregaron varias optimizaciones más nuevas para impulsar el rendimiento de carga de Next.js and
Gatsby. Este post cubre una estrategia de fragmentación granular mejorada que ahora se envía de forma predeterminada en ambos marcos.

Introduction

Como muchos frameworks web, Next.js y Gatsby utilizan web package como su agrupador principal. webpack v3 ingresado
CommonsChunkPlugin para hacer factible la salida de módulos compartidos entre diferentes puntos de entrada en un solo (o pocos) fragmentos (o fragmentos) «comunes». El código compartido se puede descargar de forma separada y almacenar en la caché del navegador desde el principio, lo que puede resultar en un mejor rendimiento de carga.

This pattern became popular with many single page app frameworks that adopted an entry point and packet configuration that looked like this:

commons-pattern-diagram-7854309

Aún cuando es práctico, el concepto de agrupar todo el código del módulo compartido en un solo fragmento tiene sus limitaciones. Los módulos no compartidos en cada punto de entrada se pueden descargar para rutas que no lo usan, lo que resulta en la descarga de más código del necesario. A modo de ejemplo, cuando page1 load the common snippet, load the code for moduleC aún cuando page1 no utiliza moduleC. For this reason, along with a few others, webpack v4 removed the plugin in favor of a new one: SplitChunksPlugin.

Improved fragmentation

The default settings for SplitChunksPlugin anda bastante bien para la mayoría de los usuarios. Se crean varios fragmentos divididos en función de una serie de terms
para evitar la consecución de código duplicado en varias rutas.

A pesar de todo, muchos frameworks web que utilizan este complemento siguen un enfoque de «común único» para la división de fragmentos. Next.js, a modo de ejemplo, generaría un commons paquete que contenía cualquier módulo que se usa en más del 50% de las páginas y todas las dependencias del marco (react, react-dom, and so).

const splitChunksConfigs = {

prod: {
chunks: 'all',
cacheGroups: {
default: false,
vendors: false,
commons: {
name: 'commons',
chunks: 'all',
minChunks: totalPages > 2 ? totalPages * 0.5 : 2,
},
react: use-subscription)[/]/,
,
},
},

Aún cuando incluir código dependiente del marco en un fragmento compartido significa que se puede descargar y almacenar en caché para cualquier punto de entrada, la heurística basada en el uso de incluir módulos comunes utilizados en más de
half the pages no es muy eficaz. La modificación de esta proporción solo daría como consecuencia uno de dos resultados:

  • If you reduce the ratio, more unnecessary code is downloaded.
  • Increasing the ratio will duplicate more code across multiple paths.

Para solucionar este problema, Next.js adoptó un configuración distinto forSplitChunksPlugin which reduces unnecessary code for any route.

  • Any sufficiently large third-party module (more than 160 KB) splits into its own individual chunk
  • A seperation frameworks a snippet is created for framework dependencies (react, react-dom, and so)
  • As many shared shards are created as needed (up to 25)
  • The minimum size to generate a fragment is changed to 20 KB

This granular fragmentation strategy provides the following benefits:

  • Page load times are improved. Broadcasting multiple shared snippets, rather than just one, minimizes the amount of unnecessary (or duplicate) code for any entry point.
  • Improved caching while browsing. La división de bibliotecas grandes y dependencias del marco en fragmentos separados reduce la oportunidad de invalidación de caché, puesto que es poco probable que ambos cambien hasta que se realice una actualización.

You can see the full configuration that Next.js adopted at webpack-config.ts.

More HTTP requests

SplitChunksPlugin definió la base para la fragmentación granular, y aplicar este enfoque a un marco como Next.js no era un concepto totalmente nuevo. A pesar de todo, muchos frameworks continuaron utilizando una única estrategia heurística y de paquetes «comunes» por algunas razones. Esto incluye la preocupación de que muchas más solicitudes HTTP puedan afectar negativamente al rendimiento del sitio.

Los navegadores solo pueden abrir un número limitado de conexiones TCP a un solo origen (6 para Chrome), por lo que minimizar el número de fragmentos generados por un agrupador puede asegurar que el número total de solicitudes permanezca por debajo de este umbral. A pesar de todo, esto solo es cierto para HTTP / 1.1. La multiplexación en HTTP / 2 posibilita transmitir diversos solicitudes en paralelo utilizando una sola conexión sobre un solo origen. Dicho de otra forma, de forma general no tenemos que preocuparnos por limitar la cantidad de fragmentos emitidos por nuestro agrupador.

All major browsers admite HTTP / 2. Los equipos de Chrome y Next.js querían ver si incrementar el número de solicitudes dividiendo el paquete «commons» único de Next.js en varios fragmentos compartidos afectaría el rendimiento de carga de alguna manera. Comenzaron midiendo el rendimiento de un solo sitio mientras modificaban el número máximo de solicitudes paralelas usando el
maxInitialRequests

property.

performance-num-requests-1-2748678

En un promedio de tres ejecuciones de diversos pruebas en una sola página web, el
load,
start-render
y los tiempos de First Contentful Paint permanecieron prácticamente iguales al variar el número máximo de solicitudes iniciales (de 5 a 15). Curiosamente, notamos una ligera sobrecarga de rendimiento solo posteriormente de dividir de forma agresiva a miles de solicitudes.

performance-num-requests-2-1561814

Esto mostró que mantenerse por debajo de un umbral confiable (20 ~ 25 solicitudes) logró el equilibrio adecuado entre el rendimiento de carga y la eficiencia del almacenamiento en caché. Posteriormente de algunas pruebas de referencia, se seleccionó 25 como maxInitialRequest tell.

Modifying the maximum number of requests that occur in parallel resulted in more than one shared package and separating them appropriately for each entry point significantly reduced the amount of unnecessary code for the same page.

js-payload-num-requests-7283783

This experiment only consisted of modifying the number of requests to see if there would be any negative effect on page load performance. The results suggest that the configuration maxInitialRequests to
25 en la página de prueba fue óptimo debido a que redujo el tamaño de la carga útil de JavaScript sin ralentizar la página. La cantidad total de JavaScript que se necesitaba para hidratar la página seguía siendo la misma, lo que explica por qué el rendimiento de carga de la página no necesariamente mejoró con la cantidad reducida de código.

webpack utiliza 30 KB como tamaño mínimo predeterminado para que se genere un fragmento. A pesar de todo, acoplar un
maxInitialRequests a value of 25 with a minimum size of 20 KB resulted in better caching.

Size reductions with granular chunks

Muchos marcos, incluido Next.js, se centran en el enrutamiento del lado del cliente (manejado por JavaScript) para inyectar etiquetas de script más nuevas para cada transición de ruta. Pero, ¿cómo predeterminan estos fragmentos dinámicos en el momento de la construcción?

Next.js utiliza un archivo de manifiesto de compilación del lado del servidor para establecer qué fragmentos generados son utilizados por diferentes puntos de entrada. Para proporcionar esta información además al cliente, se creó un archivo de manifiesto de compilación abreviado del lado del cliente para adjudicar todas las dependencias para cada punto de entrada.


getDependencies (route) {
return this.promisedBuildManifest.then(
man => (man[route] && man[route].map(dirección url => `/_next/${dirección url}`)) || []
)
}

outputted-chunks-9444947

This new granular fragmentation strategy was first implemented in Next.js behind a flag, where it was tested on several early users. Many saw significant reductions in the total JavaScript used for their entire site:

The final version was shipped by default in version 9.2.

Gatsby

Gatsby utilizado para seguir el mismo enfoque de usar una heurística basada en el uso para establecer módulos comunes:

config.optimization = {

splitChunks: {
name: false,
chunks: `all`,
cacheGroups: {
default: false,
vendors: false,
commons: {
name: `commons`,
chunks: `all`,


minChunks: componentsCount > 2 ? componentsCount * 0.5 : 2,
},
react: react-dom,

Al aprovechar al máximo la configuración de su paquete web para adoptar una estrategia de fragmentación granular semejante, además notaron reducciones considerables de JavaScript en muchos sitios grandes:

Take a look at the PR para saber cómo implementaron esta lógica en la configuración de su paquete web, que se envía de forma predeterminada en v2.20.7.

conclusion

El concepto de envío de fragmentos granulares no es específico de Next.js, Gatsby o inclusive webpack. Todo el mundo debería considerar mejorar la estrategia de fragmentación de su aplicación si sigue un gran enfoque de paquete «común», independientemente del marco o paquete de módulos utilizado.

  • If you want to see the same chunking optimizations applied to a vanilla React app, take a look at this React app sample. Usa una versión simplificada de la estrategia de fragmentación granular y puede ayudarlo a empezar a aplicar el mismo tipo de lógica a su sitio.
  • For Rollup, chunks are created granular by default. Take a look at
    manual Chunks if you want to manually configure the behavior.

R Marketing Digital