Optimiser le chargement des ressources

Dans le module précédent, nous avons exploré la théorie du chemin de rendu critique et la façon dont les ressources bloquant le rendu et l'analyse peuvent retarder le rendu initial d'une page. Maintenant que vous avez compris certains aspects théoriques, vous êtes prêt à découvrir des techniques d'optimisation du chemin de rendu critique.

Lorsqu'une page se charge, de nombreuses ressources sont référencées dans son code HTML. Elles fournissent à la page son apparence et sa mise en page grâce au CSS, ainsi que son interactivité grâce à JavaScript. Ce module aborde un certain nombre de concepts importants liés à ces ressources et à leur impact sur le temps de chargement d'une page.

Blocage du rendu

Comme indiqué dans le module précédent, le CSS est une ressource qui bloque le rendu, car il empêche le navigateur d'afficher tout contenu tant que le modèle d'objet CSS (CSSOM) n'est pas construit. Le navigateur bloque le rendu pour éviter un FOUC (Flash of Unstyled Content), qui est indésirable du point de vue de l'expérience utilisateur.

Dans la vidéo précédente, vous pouvez voir un bref FOUC où la page s'affiche sans aucun style. Ensuite, tous les styles sont appliqués une fois que le CSS de la page a fini de se charger à partir du réseau, et la version sans style de la page est immédiatement remplacée par la version stylée.

En règle générale, un FOUC est quelque chose que vous ne voyez pas normalement, mais il est important de comprendre ce concept pour savoir pourquoi le navigateur bloque le rendu de la page jusqu'à ce que le CSS soit téléchargé et appliqué à la page. Le blocage du rendu n'est pas forcément indésirable, mais vous devez minimiser sa durée en optimisant votre CSS.

Blocage de l'analyseur

Une ressource bloquant l'analyseur interrompt l'analyseur HTML, comme un élément <script> sans attributs async ni defer. Lorsque l'analyseur syntaxique rencontre un élément <script>, le navigateur doit évaluer et exécuter le script avant de poursuivre l'analyse du reste du code HTML. Ce comportement est normal, car les scripts peuvent modifier le DOM ou y accéder pendant sa construction.

<!-- This is a parser-blocking script: -->
<script src="/script.js"></script>

Lorsque vous utilisez des fichiers JavaScript externes (sans async ni defer), l'analyseur est bloqué à partir du moment où le fichier est découvert jusqu'à ce qu'il soit téléchargé, analysé et exécuté. Lorsque vous utilisez du code JavaScript intégré, l'analyseur est également bloqué jusqu'à ce que le script intégré soit analysé et exécuté.

Scanner de préchargement

Le scanner de préchargement est une optimisation du navigateur qui se présente sous la forme d'un analyseur HTML secondaire. Il analyse la réponse HTML brute pour trouver et récupérer de manière spéculative les ressources avant que l'analyseur HTML principal ne les découvre. Par exemple, le scanner de préchargement permettrait au navigateur de commencer à télécharger une ressource spécifiée dans un élément <img>, même lorsque l'analyseur HTML est bloqué lors de la récupération et du traitement de ressources telles que CSS et JavaScript.

Pour profiter du scanner de préchargement, les ressources critiques doivent être incluses dans le balisage HTML envoyé par le serveur. Les modèles de chargement de ressources suivants ne sont pas détectables par l'analyseur de préchargement :

  • Images chargées par CSS à l'aide de la propriété background-image. Ces références d'images se trouvent dans le CSS et ne peuvent pas être détectées par le scanner de préchargement.
  • Scripts chargés dynamiquement sous la forme d'un balisage d'élément <script> injecté dans le DOM à l'aide de JavaScript ou de modules chargés à l'aide de import() dynamique.
  • HTML affiché sur le client à l'aide de JavaScript. Ce balisage est contenu dans des chaînes de ressources JavaScript et n'est pas détectable par le scanner de préchargement.
  • Déclarations @import CSS.

Ces modèles de chargement de ressources sont tous des ressources découvertes tardivement et ne bénéficient donc pas du scanner de préchargement. Évitez-les autant que possible. Toutefois, si vous ne pouvez pas éviter ces schémas, vous pouvez utiliser un indice preload pour éviter les retards de découverte des ressources.

CSS

Le CSS détermine la présentation et la mise en page d'une page. Comme décrit précédemment, le CSS est une ressource bloquant le rendu. L'optimisation de votre CSS peut donc avoir un impact considérable sur le temps de chargement global de la page.

Minification

La minimisation des fichiers CSS réduit la taille d'une ressource CSS, ce qui accélère leur téléchargement. Pour ce faire, il supprime principalement le contenu d'un fichier CSS source, comme les espaces et autres caractères invisibles, et génère le résultat dans un nouveau fichier optimisé :

/* Unminified CSS: */

/* Heading 1 */
h1 {
  font-size: 2em;
  color: #000000;
}

/* Heading 2 */
h2 {
  font-size: 1.5em;
  color: #000000;
}
/* Minified CSS: */
h1,h2{color:#000}h1{font-size:2em}h2{font-size:1.5em}

Dans sa forme la plus élémentaire, la minification CSS est une optimisation efficace qui peut améliorer le FCP de votre site Web, et peut-être même le LCP dans certains cas. Des outils tels que les regroupeurs peuvent effectuer automatiquement cette optimisation pour vous dans les builds de production.

Supprimer les ressources CSS inutilisées

Avant d'afficher un contenu, le navigateur doit télécharger et analyser toutes les feuilles de style. Le temps nécessaire à l'analyse inclut également les styles inutilisés sur la page actuelle. Si vous utilisez un bundler qui combine toutes les ressources CSS dans un seul fichier, il est probable que vos utilisateurs téléchargent plus de CSS que nécessaire pour afficher la page actuelle.

Pour découvrir les CSS inutilisés de la page actuelle, utilisez l'outil de couverture dans Chrome DevTools.

Capture d&#39;écran de l&#39;outil de couverture dans les outils pour les développeurs Chrome. Un fichier CSS est sélectionné dans son volet inférieur, ce qui montre une quantité considérable de CSS inutilisés par la mise en page actuelle.
L'outil de couverture des outils pour les développeurs Chrome est utile pour détecter les feuilles de style CSS (et JavaScript) inutilisées par la page actuelle. Il peut être utilisé pour diviser les fichiers CSS en plusieurs ressources à charger par différentes pages, au lieu d'envoyer un bundle CSS beaucoup plus volumineux qui peut retarder le rendu de la page.

La suppression du CSS inutilisé a un double effet : en plus de réduire le temps de téléchargement, vous optimisez la construction de l'arborescence de rendu, car le navigateur a besoin de traiter moins de règles CSS.

Éviter les déclarations CSS @import

Bien que cela puisse sembler pratique, vous devez éviter les déclarations @import dans le code CSS :

/* Don't do this: */
@import url('style.css');

De la même manière que l'élément <link> fonctionne en HTML, la déclaration @import en CSS vous permet d'importer une ressource CSS externe à partir d'une feuille de style. La principale différence entre ces deux approches est que l'élément HTML <link> fait partie de la réponse HTML et est donc découvert beaucoup plus tôt qu'un fichier CSS téléchargé par une déclaration @import.

En effet, pour qu'une déclaration @import soit détectée, le fichier CSS qui la contient doit d'abord être téléchargé. Cela entraîne ce que l'on appelle une chaîne de requêtes qui, dans le cas du CSS, retarde le temps nécessaire au rendu initial d'une page. Un autre inconvénient est que les feuilles de style chargées à l'aide d'une déclaration @import ne peuvent pas être découvertes par le scanner de préchargement et deviennent donc des ressources de blocage du rendu découvertes tardivement.

<!-- Do this instead: -->
<link rel="stylesheet" href="style.css">

Dans la plupart des cas, vous pouvez remplacer @import par un élément <link rel="stylesheet">. Les éléments <link> permettent de télécharger les feuilles de style simultanément et de réduire le temps de chargement global, contrairement aux déclarations @import, qui téléchargent les feuilles de style consécutivement.

Intégrer les fichiers CSS critiques

Le temps nécessaire pour télécharger les fichiers CSS peut augmenter le FCP d'une page. L'intégration des styles critiques dans le document <head> élimine la requête réseau pour une ressource CSS et, lorsqu'elle est effectuée correctement, peut améliorer les temps de chargement initiaux lorsque le cache du navigateur d'un utilisateur n'est pas amorcé. Le reste du code CSS peut être chargé de manière asynchrone ou ajouté à la fin de l'élément <body>.

<head>
  <title>Page Title</title>
  <!-- ... -->
  <style>h1,h2{color:#000}h1{font-size:2em}h2{font-size:1.5em}</style>
</head>
<body>
  <!-- Other page markup... -->
  <link rel="stylesheet" href="non-critical.css">
</body>

L'inconvénient est que l'intégration d'une grande quantité de CSS ajoute des octets à la réponse HTML initiale. Étant donné que les ressources HTML ne peuvent souvent pas être mises en cache très longtemps, voire pas du tout, cela signifie que le CSS intégré n'est pas mis en cache pour les pages suivantes qui peuvent utiliser le même CSS dans des feuilles de style externes. Testez et mesurez les performances de votre page pour vous assurer que les compromis valent la peine.

Démos CSS

JavaScript

JavaScript est à l'origine de la plupart des interactions sur le Web, mais il a un coût. Si vous envoyez trop de code JavaScript, votre page Web peut mettre du temps à répondre lors du chargement de la page, et peut même entraîner des problèmes de réactivité qui ralentissent les interactions. Ces deux problèmes peuvent être frustrants pour les utilisateurs.

JavaScript bloquant l'affichage

Lorsque des éléments <script> sont chargés sans les attributs defer ni async, le navigateur bloque l'analyse et le rendu jusqu'à ce que le script soit téléchargé, analysé et exécuté. De même, les scripts intégrés bloquent l'analyseur jusqu'à ce que le script soit analysé et exécuté.

async et defer

async et defer permettent aux scripts externes de se charger sans bloquer l'analyseur HTML, tandis que les scripts (y compris les scripts intégrés) avec type="module" sont différés automatiquement. Toutefois, il est important de comprendre certaines différences entre async et defer.

Représentation de différents mécanismes de chargement de script, détaillant tous les rôles d&#39;analyseur, de récupération et d&#39;exécution en fonction de divers attributs utilisés tels que async, defer, type=&#39;module&#39; et une combinaison des trois.
Source : https://html.spec.whatwg.org/multipage/scripting.html

Les scripts chargés avec async sont analysés et exécutés immédiatement après leur téléchargement, tandis que ceux chargés avec defer sont exécutés une fois l'analyse du document HTML terminée. Cela se produit en même temps que l'événement DOMContentLoaded du navigateur. De plus, les scripts async peuvent s'exécuter dans le désordre, tandis que les scripts defer sont exécutés dans l'ordre dans lequel ils apparaissent dans le balisage.

Affichage côté client

En règle générale, vous devez éviter d'utiliser JavaScript pour afficher du contenu critique ou l'élément LCP d'une page. C'est ce qu'on appelle le rendu côté client, une technique largement utilisée dans les applications monopages (SPA).

Le balisage affiché par JavaScript contourne le scanner de préchargement, car les ressources contenues dans le balisage affiché côté client ne sont pas détectables par celui-ci. Cela peut retarder le téléchargement de ressources cruciales, comme une image LCP. Le navigateur ne commence à télécharger l'image LCP qu'une fois le script exécuté et l'élément ajouté au DOM. À son tour, le script ne peut être exécuté qu'après avoir été découvert, téléchargé et analysé. On parle alors de chaîne de demandes critiques, qui doit être évitée.

De plus, le rendu du balisage à l'aide de JavaScript est plus susceptible de générer des tâches longues que le balisage téléchargé depuis le serveur en réponse à une requête de navigation. L'utilisation intensive du rendu HTML côté client peut avoir un impact négatif sur la latence d'interaction. C'est particulièrement vrai dans les cas où le DOM d'une page est très volumineux, ce qui déclenche un travail de rendu important lorsque JavaScript modifie le DOM.

Minification

Comme pour le CSS, la minimisation de JavaScript réduit la taille du fichier d'une ressource de script. Cela peut accélérer les téléchargements, ce qui permet au navigateur de passer plus rapidement au processus d'analyse et de compilation de JavaScript.

De plus, la minimisation de JavaScript va plus loin que celle des autres éléments, comme le CSS. Lorsque JavaScript est réduit, il est non seulement débarrassé des espaces, des tabulations et des commentaires, mais les symboles du code JavaScript source sont également raccourcis. Ce processus est parfois appelé uglification. Pour voir la différence, prenez le code source JavaScript suivant :

// Unuglified JavaScript source code:
export function injectScript () {
  const scriptElement = document.createElement('script');
  scriptElement.src = '/js/scripts.js';
  scriptElement.type = 'module';

  document.body.appendChild(scriptElement);
}

Lorsque le code source JavaScript précédent est minimisé, le résultat peut ressembler à l'extrait de code suivant :

// Uglified JavaScript production code:
export function injectScript(){const t=document.createElement("script");t.src="/js/scripts.js",t.type="module",document.body.appendChild(t)}

Dans l'extrait précédent, vous pouvez voir que la variable lisible par l'utilisateur scriptElement dans la source est abrégée en t. Lorsqu'elle est appliquée à une grande collection de scripts, les économies peuvent être assez importantes, sans affecter les fonctionnalités fournies par le JavaScript de production d'un site Web.

Si vous utilisez un bundler pour traiter le code source de votre site Web, l'uglification est souvent effectuée automatiquement pour les builds de production. Les outils d'optimisation, tels que Terser, sont également très configurables, ce qui vous permet d'ajuster l'agressivité de l'algorithme d'optimisation pour réaliser un maximum d'économies. Toutefois, les paramètres par défaut de tout outil d'optimisation sont généralement suffisants pour trouver le juste équilibre entre la taille de la sortie et la préservation des fonctionnalités.

Démos JavaScript

Tester vos connaissances

Quel est le meilleur moyen de charger plusieurs fichiers CSS dans le navigateur ?

Déclaration CSS @import.
Réessayez.
Plusieurs éléments <link>.
Bonne réponse !

Que fait le scanner de préchargement du navigateur ?

Il s'agit d'un analyseur HTML secondaire qui examine le balisage brut pour découvrir les ressources avant l'analyseur DOM, afin de les découvrir plus tôt.
Bonne réponse !
Détecte les éléments <link rel="preload"> dans une ressource HTML.
Réessayez.

Pourquoi le navigateur bloque-t-il temporairement l'analyse du code HTML par défaut lors du téléchargement de ressources JavaScript ?

Pour éviter un FOUC (Flash of Unstyled Content).
Réessayez.
Étant donné que l'évaluation de JavaScript est une tâche très gourmande en ressources processeur, la mise en pause de l'analyse HTML donne plus de bande passante au processeur pour terminer le chargement des scripts.
Réessayez.
En effet, les scripts peuvent modifier le DOM ou y accéder.
Bonne réponse !

Prochaine étape : Aider le navigateur avec des indications de ressources

Maintenant que vous avez compris comment les ressources chargées dans l'élément <head> peuvent affecter le chargement initial de la page et diverses métriques, il est temps de passer à la suite. Le module suivant explore les indications de ressources et explique comment elles peuvent fournir des indications précieuses au navigateur pour qu'il commence à charger les ressources et à ouvrir des connexions aux serveurs d'origine croisée plus tôt qu'il ne le ferait sans elles.