Une nouvelle stratégie de fragmentation du webpack dans Next.js et Gatsby limite le code en double pour améliorer les performances de chargement des pages.
Chrome collabore avec des outils et dans l'écosystème Open Source JavaScript. De nouvelles optimisations, pour améliorer les performances de chargement de Next.js et Gatsby Cet article aborde une meilleure stratégie de fragmentation précise qui est désormais fourni par défaut dans les deux frameworks.
Introduction
Comme de nombreux frameworks Web, Next.js et Gatsby utilisent le webpack comme outil principal
bundler. Lancement de webpack v3
CommonsChunkPlugin
pour permettre
modules de sortie partagés entre différents points d'entrée dans un seul (ou quelques) "commons" (ou
fragments). Le code partagé peut être téléchargé séparément et stocké tôt dans le cache du navigateur, ce qui peut
améliorent les performances de chargement.
Ce modèle est devenu populaire avec de nombreux frameworks d'applications monopages adoptant un point d'entrée et de la configuration du bundle, qui se présentait comme suit:
Bien que pratique, le concept de regroupement de tout le code de module partagé en un seul bloc a ses
limites. Les modules qui ne sont pas partagés dans tous les points d'entrée peuvent être téléchargés pour les routes qui ne les utilisent pas
ce qui entraîne le téléchargement de plus
de code que nécessaire. Par exemple, lorsque page1
charge
le bloc common
, il charge le code pour moduleC
même si page1
n'utilise pas moduleC
.
C'est pourquoi, comme quelques autres, webpack v4 a supprimé le plug-in en faveur d'un nouveau
un: SplitChunksPlugin
.
Fractionnement amélioré
Les paramètres par défaut de SplitChunksPlugin
fonctionnent bien pour la plupart des utilisateurs. Plusieurs fragments divisés sont
créés en fonction d'un certain nombre de conditions ;
pour éviter de récupérer du code en double sur plusieurs routes.
Cependant, de nombreux frameworks Web qui utilisent ce plug-in suivent toujours un "Single-Commons" de fragmentation
le fractionnement. Next.js, par exemple, générerait un bundle commons
contenant tout module
utilisée dans plus de 50% des pages et dans toutes les dépendances du framework (react
, react-dom
, etc.).
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)[\\/]/,
},
},
},
Bien que le fait d'inclure du code dépendant du framework dans un bloc partagé signifie qu'il peut être téléchargé et mis en cache pour tout point d'entrée, l'heuristique basée sur l'utilisation qui consiste à inclure les modules courants utilisés dans plus de moitié des pages n'est pas très efficace. La modification de ce ratio n'entraînerait que l'un des deux résultats suivants:
- Si vous réduisez ce ratio, davantage de code inutile est téléchargé.
- Si vous augmentez le ratio, une plus grande quantité de code est dupliquée pour plusieurs itinéraires.
Pour résoudre ce problème, Next.js a adopté un autre
pour SplitChunksPlugin
qui réduit
du code inutile pour
n’importe quelle route.
- Tout module tiers suffisamment volumineux (supérieur à 160 Ko) est divisé en un module distinct fragment
- Un bloc
frameworks
distinct est créé pour les dépendances du framework (react
,react-dom
et etc.) - Autant de fragments partagés que nécessaire sont créés (jusqu'à 25)
- La taille minimale d'un fragment à générer est fixée à 20 Ko.
Cette stratégie de fragmentation précise offre les avantages suivants:
- Les temps de chargement des pages sont réduits. Émettre plusieurs fragments partagés, au lieu d'un seul, réduit la quantité de code inutile (ou en double) pour chaque point d'entrée.
- Amélioration de la mise en cache lors des navigations. Diviser les bibliothèques volumineuses et les dépendances de framework en fragments distincts réduit le risque d'invalidation du cache, car il est peu probable modifier jusqu'à ce qu'une mise à niveau soit effectuée.
Vous pouvez voir l'intégralité de la configuration adoptée par Next.js dans webpack-config.ts
.
Autres requêtes HTTP
SplitChunksPlugin
a défini la base d'une fragmentation précise, en appliquant cette approche à une
comme Next.js,
n'était pas un concept entièrement nouveau. Toutefois, de nombreux frameworks ont continué à
utiliser une seule heuristique et
« commons » pour plusieurs raisons. Cela inclut la préoccupation que
de nombreuses autres requêtes HTTP
peuvent avoir un impact négatif sur les performances du site.
Les navigateurs ne peuvent ouvrir qu'un nombre limité de connexions TCP vers une seule origine (six pour Chrome). réduire le nombre de fragments générés par un bundler permet de garantir que le nombre total de requêtes reste en deçà de ce seuil. Toutefois, cela ne s'applique qu'à HTTP/1.1. Multiplexage en HTTP/2 permet de diffuser plusieurs requêtes en parallèle à l'aide d'une seule connexion sur une même origine. En d'autres termes, nous n'avons généralement pas à nous soucier de limiter le nombre de fragments émises par notre bundler.
Tous les principaux navigateurs sont compatibles avec HTTP/2. Les équipes Chrome et Next.js
voulait savoir si l'augmentation du nombre de requêtes en divisant le seul "commons" de Next.js lot
en plusieurs fragments partagés affecterait
de quelque manière que ce soit les performances de chargement. Elle a commencé par mesurer
les performances d'un seul site tout en modifiant le nombre maximal de requêtes parallèles à l'aide du
maxInitialRequests
.
En moyenne, trois essais portant sur une même page Web ont été exécutés
load
,
start-render
et les heures de First Contentful Paint sont toutes restées à peu près les mêmes lorsque la valeur
le nombre de requêtes (de 5 à 15). Fait intéressant, nous avons remarqué
une légère baisse des performances
après avoir divisé de façon agressive
des centaines de requêtes.
Cela a montré que rester en dessous d'un seuil fiable (20 à 25 requêtes) offrait le bon équilibre.
entre les performances de chargement
et l'efficacité de la mise en cache. Après quelques tests de référence, 25 ont été choisies comme
le nombre de maxInitialRequest
.
La modification du nombre maximal de requêtes exécutées en parallèle a entraîné la création de plusieurs requêtes. partagé et en les séparant de façon appropriée pour chaque point d'entrée, ce qui a permis de code inutile pour une même page.
Ce test visait uniquement à modifier le nombre de demandes pour voir s'il y aurait
peut avoir un impact négatif
sur les performances de chargement des pages. Les résultats suggèrent de définir maxInitialRequests
sur
25
sur la page de test était optimal, car il a réduit la taille de la charge utile JavaScript sans ralentir
vers le bas de la page. La quantité totale de JavaScript nécessaire pour hydrater la page est restée
ce qui explique pourquoi le chargement des pages ne s'est pas forcément amélioré
quantité de code.
webpack utilise une taille minimale de 30 Ko par défaut pour qu'un fragment soit généré. En revanche, associer
La valeur maxInitialRequests
de 25 avec une taille minimale de 20 Ko a plutôt permis d'améliorer la mise en cache.
Réductions de la taille avec des fragments précis
De nombreux frameworks, y compris Next.js, s'appuient sur le routage côté client (géré par JavaScript) pour injecter des tags de script plus récents pour chaque transition de route. Mais comment prédéterminer ces blocs dynamiques au moment de la compilation ?
Next.js utilise un fichier manifeste de compilation côté serveur pour déterminer les fragments en sortie utilisés par différents points d'entrée. Pour fournir également ces informations au client, une version abrégée côté client fichier manifeste de compilation a été créé pour mapper toutes les dépendances de chaque point d'entrée.
// 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}`)) || []
)
}
Cette nouvelle stratégie de fragmentation granulaire a d'abord été déployée dans Next.js derrière un indicateur, où elle a été testée sur un le nombre d'utilisateurs de la première heure. Nombre d'entre eux ont constaté une réduction significative de la quantité totale de JavaScript utilisée pour leur tout le site:
Site Web | Variation JS totale | Différence (en %) |
---|---|---|
https://www.barnebys.com/ | -238 Ko | -23% |
https://sumup.com/ | -220 Ko | -30% |
https://www.hashicorp.com/ | -11 Mo | -71% |
La version finale a été envoyée par défaut dans la version 9.2.
Gatsby
Gatsby avait l'habitude de suivre la même approche, qui consistait à utiliser heuristique pour définir des modules courants:
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)[\\/]/,
},
En optimisant la configuration de son webpack pour adopter une stratégie de fragmentation granulaire similaire, l'entreprise a également a constaté une réduction importante de l'utilisation de JavaScript sur de nombreux sites de grande taille:
Site Web | Variation JS totale | Différence (en %) |
---|---|---|
https://www.gatsbyjs.org/ | -680 Ko | -22% |
https://www.thirdandgrove.com/ | -390 Ko | -25 % |
https://ghost.org/ | -1,1 Mo | -35% |
https://reactjs.org/ | -80 Ko | - 8 % |
Consultez les RP pour comprendre implémenté cette logique dans sa configuration webpack, qui est livrée par défaut dans la version 2.20.7.
Conclusion
Le concept d'envoi de fragments granulaires n'est pas spécifique à Next.js, Gatsby ni même Webpack. Tout le monde devraient envisager d'améliorer la stratégie de fragmentation de leur application si elle suit un grand nombre quel que soit le framework ou le bundler utilisé.
- Si vous souhaitez appliquer les mêmes optimisations de fragmentation à une application vanilla React, consultez cet exemple de code l'application Nest. Elle utilise un simplifiée de la stratégie de fragmentation granulaire et peut vous aider à commencer à appliquer la même une sorte de logique à votre site.
- Pour le Rollup, les fragments sont créés de manière détaillée par défaut. Jetez un œil à
manualChunks
si vous souhaitez configurer le comportement.