Una nueva estrategia de fragmentación de webpack en Next.js y Gatsby minimiza el código duplicado para mejorar el rendimiento de carga de la página.
Chrome está colaborando con herramientas y en el ecosistema de código abierto de JavaScript. Recientemente, se implementaron varias optimizaciones para mejorar el rendimiento de carga de Next.js y Gatsby: En este artículo, se describe una estrategia de fragmentación detallada mejorada que ahora se envía de forma predeterminada en ambos frameworks.
Introducción
Al igual que muchos frameworks web, Next.js y Gatsby usan webpack como su núcleo
agrupador. Presentación de webpack v3
CommonsChunkPlugin
para que sea posible
módulos de salida compartidos entre diferentes puntos de entrada en un solo (o pocos) “comunes” fragmento (o
en bloques). El código compartido puede descargarse por separado y almacenarse en la memoria caché del navegador con anticipación, lo que puede
un mejor rendimiento de carga.
Este patrón se volvió popular entre muchos frameworks de aplicaciones de una sola página que adoptaban un punto de entrada y del paquete con el siguiente aspecto:
Aunque resulta práctico, el concepto de agrupar todo el código del módulo compartido en un solo bloque tiene su
las limitaciones de seguridad. Los módulos que no se comparten en todos los puntos de entrada se pueden descargar para las rutas que no lo utilizan.
lo que provocará que se descargue más código del necesario. Por ejemplo, cuando se carga page1
el bloque common
, carga el código para moduleC
aunque page1
no use moduleC
.
Por esta razón, junto con otros, webpack v4 quitó el complemento para dar lugar a un nuevo
uno: SplitChunksPlugin
.
Fragmentación mejorada
La configuración predeterminada de SplitChunksPlugin
funciona bien para la mayoría de los usuarios. Múltiples bloques divididos son
que se crean según una serie de condiciones
para evitar recuperar código duplicado en múltiples rutas.
Sin embargo, muchos frameworks web que usan este complemento siguen un único elemento común enfoque para fragmentar
la división. Por ejemplo, Next.js generaría un paquete commons
con cualquier módulo que se
Se usa en más del 50% de las páginas y en todas las dependencias del framework (react
, react-dom
, etcétera).
const splitChunksConfigs = {
…
prod: {
chunks: 'all',
cacheGroups: {
default: false,
vendors: false,
commons: {
name: 'commons',
chunks: 'all',
minChunks: totalPages > 2 ? totalPages * 0.5 : 2,
},
react: {
name: 'commons',
chunks: 'all',
test: /[\\/]node_modules[\\/](react|react-dom|scheduler|use-subscription)[\\/]/,
},
},
},
Aunque incluir un código que depende del framework en un bloque compartido significa que se puede descargar y en caché para cualquier punto de entrada, la heurística basada en el uso de incluir módulos comunes utilizados en más de la mitad de las páginas no es muy eficaz. Modificar esta proporción solo daría como resultado uno de dos resultados:
- Si reduces la proporción, se descargará más código innecesario.
- Si aumentas la proporción, se duplica más código en múltiples rutas.
Para resolver este problema, Next.js adoptó una forma diferente
virtual para SplitChunksPlugin
que reduce
código innecesario para ninguna ruta.
- Cualquier módulo de terceros lo suficientemente grande (más de 160 KB) se divide en su propio fragmento
- Se crea un fragmento
frameworks
separado para las dependencias del framework (react
,react-dom
y así sucesivamente). - Se crean tantos fragmentos compartidos como sea necesario (hasta 25)
- El tamaño mínimo para generar un fragmento cambió a 20 KB.
Esta estrategia de fragmentación detallada proporciona los siguientes beneficios:
- Se mejoran los tiempos de carga de la página. Emitir varios fragmentos compartidos, en lugar de uno solo, minimiza la cantidad de código innecesario (o duplicado) para cualquier punto de entrada.
- Almacenamiento en caché mejorado durante las navegaciones. Divide bibliotecas grandes y dependencias de framework en fragmentos separados reduce la posibilidad de invalidar la caché, ya que es poco probable que hasta que se realice una actualización.
Puedes ver toda la configuración que Next.js adoptó en webpack-config.ts
.
Más solicitudes HTTP
SplitChunksPlugin
definió la base de la fragmentación detallada y, luego, aplicar este enfoque a un
como Next.js no era un concepto completamente nuevo. Sin embargo, muchos frameworks aún siguieron
usar una heurística única y “comunes” de paquetes de aplicaciones por varios motivos. Esto incluye la preocupación de que
muchas más solicitudes HTTP pueden afectar
el rendimiento del sitio de forma negativa.
Los navegadores solo pueden abrir una cantidad limitada de conexiones TCP a un único origen (6 para Chrome), por lo que minimizar la cantidad de fragmentos que genera un agrupador puede garantizar que el número se mantenga por debajo de este umbral. Sin embargo, esto solo es válido para HTTP/1.1. Multiplexación en HTTP/2 permite transmitir múltiples solicitudes en paralelo mediante una única conexión en una sola origen. En otras palabras, no debemos preocuparnos de limitar el número de fragmentos emitido por nuestro agrupador.
Todos los navegadores principales admiten HTTP/2. Los equipos de Chrome y Next.js
quería ver si aumentar la cantidad de solicitudes dividiendo los “commons” individuales de Next.js paquete
en varios fragmentos compartidos afectaría el rendimiento de carga de cualquier manera. Comenzaron midiendo
de un solo sitio y, a la vez, se modifica la cantidad máxima de solicitudes paralelas mediante
maxInitialRequests
propiedad.
En un promedio de tres ejecuciones de varias pruebas en una sola página web, el
load
,
start-render
y los tiempos de First Contentful Paint se mantuvieron casi iguales cuando se varió el valor inicial máximo
de solicitudes (de 5 a 15). Curiosamente, notamos una leve sobrecarga de rendimiento solo
luego de dividirla de forma agresiva en cientos de solicitudes.
Esto demostró que mantenerse por debajo de un umbral confiable (entre 20 y 25 solicitudes) lograba encontrar el equilibrio adecuado.
entre el rendimiento de carga y la eficiencia del almacenamiento en caché. Después de algunas pruebas de referencia, se seleccionaron 25
el recuento de maxInitialRequest
.
Modificar la cantidad máxima de solicitudes que ocurren en paralelo generó más de una y separarlos de forma apropiada para cada punto de entrada redujo cantidad de código innecesario para la misma página.
Este experimento solo consistió en modificar la cantidad de solicitudes para ver si habría alguna
un efecto negativo en el rendimiento de carga de la página. Los resultados sugieren que configurar maxInitialRequests
como
25
en la página de prueba fue óptimo, ya que redujo el tamaño de la carga útil de JavaScript sin disminuir la velocidad.
en toda la página. Se mantuvo la cantidad total de JavaScript que se necesitaba para hidratar la página.
prácticamente igual, lo que explica por qué el rendimiento
de la carga de la página no mejoró necesariamente
cantidad de código.
webpack usa 30 KB como tamaño mínimo predeterminado para que se genere un fragmento. Sin embargo, si se acopla un
Un valor maxInitialRequests
de 25 con un tamaño mínimo de 20 KB en su lugar, dio como resultado un mejor almacenamiento en caché.
Reducciones de tamaño con fragmentos detallados
Muchos frameworks, incluido Next.js, dependen del enrutamiento del cliente (controlado por JavaScript) para insertar nuevas etiquetas de secuencia de comandos para cada transición de ruta. Pero ¿cómo se predefinen estos fragmentos dinámicos en el tiempo de compilación?
Next.js usa un archivo de manifiesto de compilación del lado del servidor para determinar qué fragmentos de salida usan puntos de entrada diferentes. Para proporcionarle al cliente esta información, un resumen del cliente el archivo de manifiesto de compilación se creó para asignar todas las dependencias de cada punto de entrada.
// Returns a promise for the dependencies for a particular route
getDependencies (route) {
return this.promisedBuildManifest.then(
man => (man[route] && man[route].map(url => `/_next/${url}`)) || []
)
}
![Resultado de varios fragmentos compartidos en una app de Next.js.](https://web.dev/static/articles/granular-chunking-nextjs/image/output-multiple-shared-c-772d7dc79d8af.png?authuser=5&hl=es)
Esta nueva estrategia de fragmentación detallada se lanzó por primera vez en Next.js detrás de una marca, donde se probó en una la cantidad de usuarios pioneros. Muchos experimentaron reducciones significativas en el total de JavaScript que se usó para sus todo el sitio:
Sitio web | Cambio total de JS | % de diferencia |
---|---|---|
https://www.barnebys.com/ | -238 KB | −23% |
https://sumup.com/ | -220 KB | −30% |
https://www.hashicorp.com/ | -11 MB | −71% |
La versión final se envió de forma predeterminada en la versión 9.2.
Gatsby
Gatsby solía seguir el mismo enfoque de utilizar un sistema de heurística para definir módulos comunes:
config.optimization = {
…
splitChunks: {
name: false,
chunks: `all`,
cacheGroups: {
default: false,
vendors: false,
commons: {
name: `commons`,
chunks: `all`,
// if a chunk is used more than half the components count,
// we can assume it's pretty global
minChunks: componentsCount > 2 ? componentsCount * 0.5 : 2,
},
react: {
name: `commons`,
chunks: `all`,
test: /[\\/]node_modules[\\/](react|react-dom|scheduler)[\\/]/,
},
Al optimizar su configuración de webpack para adoptar una estrategia de fragmentación detallada similar, también notó reducciones considerables de JavaScript en muchos sitios grandes:
Sitio web | Cambio total de JS | % de diferencia |
---|---|---|
https://www.gatsbyjs.org/ | -680 KB | −22% |
https://www.thirdandgrove.com/ | -390 KB | -25% |
https://ghost.org/ | -1.1 MB | −35% |
https://reactjs.org/ | -80 KB | -8% |
Consulta el documento de RR.PP. para comprender cómo implementó esta lógica en su configuración de webpack, que se incluye de forma predeterminada en la versión v2.20.7.
Conclusión
El concepto de envío de fragmentos detallados no es específico de Next.js, Gatsby ni webpack. Todos deberían considerar mejorar la estrategia de fragmentación de su aplicación si sigue un comportamiento común. de paquetes de aplicaciones, sin importar el framework o agrupador de módulos que se use.
- Si deseas ver las mismas optimizaciones de fragmentación aplicadas a una aplicación de React normal, observa este ejemplo de React de la app. Utiliza un más simple de la estrategia de fragmentación detallada y puede ayudarte a comenzar a aplicar la misma tipo de lógica a tu sitio.
- En el caso de Rollup, los fragmentos se crean de forma detallada de forma predeterminada. Echa un vistazo a
manualChunks
si deseas hacerlo manualmente configurar el comportamiento.