Performances frontales 2021 : optimisations de livraison
Publié: 2022-03-10Ce guide a été aimablement soutenu par nos amis de LogRocket, un service qui combine la surveillance des performances frontales, la relecture de session et l'analyse des produits pour vous aider à créer de meilleures expériences client. LogRocket suit les mesures clés, y compris. DOM complet, temps jusqu'au premier octet, premier délai d'entrée, CPU client et utilisation de la mémoire. Obtenez un essai gratuit de LogRocket dès aujourd'hui.
Table des matières
- Se préparer : planification et métriques
- Fixer des objectifs réalistes
- Définir l'environnement
- Optimisations des actifs
- Construire des optimisations
- Optimisations de livraison
- Mise en réseau, HTTP/2, HTTP/3
- Test et surveillance
- Victoires rapides
- Tout sur une seule page
- Télécharger la liste de contrôle (PDF, pages Apple, MS Word)
- Abonnez-vous à notre newsletter par e-mail pour ne pas manquer les prochains guides.
Optimisations de livraison
- Utilisons-nous le
defer
pour charger le JavaScript critique de manière asynchrone ?
Lorsque l'utilisateur demande une page, le navigateur récupère le HTML et construit le DOM, puis récupère le CSS et construit le CSSOM, puis génère un arbre de rendu en faisant correspondre le DOM et le CSSOM. Si un code JavaScript doit être résolu, le navigateur ne commencera pas à rendre la page tant qu'il n'est pas résolu, ce qui retarde le rendu. En tant que développeurs, nous devons dire explicitement au navigateur de ne pas attendre et de commencer à rendre la page. La façon de procéder pour les scripts est d'utiliser les attributsdefer
etasync
en HTML.En pratique, il s'avère qu'il est préférable d'utiliser
defer
au lieu deasync
. Ah, c'est quoi la différence déjà ? Selon Steve Souders, une fois que les scriptsasync
arrivent, ils sont exécutés immédiatement - dès que le script est prêt. Si cela se produit très rapidement, par exemple lorsque le script est déjà dans le cache, il peut en fait bloquer l'analyseur HTML. Avecdefer
, le navigateur n'exécute pas les scripts tant que le HTML n'est pas analysé. Donc, à moins que vous ayez besoin de JavaScript pour s'exécuter avant de commencer le rendu, il est préférable d'utiliserdefer
. En outre, plusieurs fichiers asynchrones s'exécuteront dans un ordre non déterministe.Il convient de noter qu'il existe quelques idées fausses sur
async
etdefer
. Plus important encore,async
ne signifie pas que le code s'exécutera dès que le script sera prêt ; cela signifie qu'il s'exécutera chaque fois que les scripts seront prêts et que tout le travail de synchronisation précédent sera terminé. Selon les mots de Harry Roberts, "Si vous placez un scriptasync
après les scripts de synchronisation, votre scriptasync
n'est aussi rapide que votre script de synchronisation le plus lent."De plus, il n'est pas recommandé d'utiliser à la fois
async
etdefer
. Les navigateurs modernes prennent en charge les deux, mais chaque fois que les deux attributs sont utilisés,async
l'emportera toujours.Si vous souhaitez plonger dans plus de détails, Milica Mihajlija a écrit un guide très détaillé sur Construire le DOM plus rapidement, en entrant dans les détails de l'analyse spéculative, asynchrone et différée.
- Chargement paresseux de composants coûteux avec IntersectionObserver et des conseils de priorité.
En général, il est recommandé de charger paresseusement tous les composants coûteux, tels que le JavaScript lourd, les vidéos, les iframes, les widgets et potentiellement les images. Le chargement paresseux natif est déjà disponible pour les images et les iframes avec l'attribut deloading
(uniquement Chromium). Sous le capot, cet attribut diffère le chargement de la ressource jusqu'à ce qu'elle atteigne une distance calculée de la fenêtre d'affichage.<!-- Lazy loading for images, iframes, scripts. Probably for images outside of the viewport. --> <img loading="lazy" ... /> <iframe loading="lazy" ... /> <!-- Prompt an early download of an asset. For critical images, eg hero images. --> <img loading="eager" ... /> <iframe loading="eager" ... />
Ce seuil dépend de quelques éléments, du type de ressource d'image récupérée au type de connexion effectif. Mais les expériences menées à l'aide de Chrome sur Android suggèrent que sur la 4G, 97,5 % des images en dessous de la ligne de flottaison qui sont chargées paresseusement ont été entièrement chargées dans les 10 ms suivant leur apparition, elles devraient donc être sûres.
Nous pouvons également utiliser l'attribut
importance
(high
oulow
) sur un élément<script>
,<img>
ou<link>
(Blink uniquement). En fait, c'est un excellent moyen de déprioriser les images dans les carrousels, ainsi que de re-prioriser les scripts. Cependant, nous pourrions parfois avoir besoin d'un contrôle un peu plus granulaire.<!-- When the browser assigns "High" priority to an image, but we don't actually want that. --> <img src="less-important-image.svg" importance="low" ... /> <!-- We want to initiate an early fetch for a resource, but also deprioritize it. --> <link rel="preload" importance="low" href="/script.js" as="script" />
La façon la plus performante d'effectuer un chargement paresseux légèrement plus sophistiqué consiste à utiliser l'API Intersection Observer qui permet d' observer de manière asynchrone les changements dans l'intersection d'un élément cible avec un élément ancêtre ou avec la fenêtre d'affichage d'un document de niveau supérieur. Fondamentalement, vous devez créer un nouvel objet
IntersectionObserver
, qui reçoit une fonction de rappel et un ensemble d'options. Ensuite, nous ajoutons une cible à observer.La fonction de rappel s'exécute lorsque la cible devient visible ou invisible, donc lorsqu'elle intercepte la fenêtre d'affichage, vous pouvez commencer à prendre certaines mesures avant que l'élément ne devienne visible. En fait, nous avons un contrôle granulaire sur le moment où le rappel de l'observateur doit être invoqué, avec
rootMargin
(marge autour de la racine) et lethreshold
(un nombre unique ou un tableau de nombres qui indiquent à quel pourcentage de la visibilité de la cible nous visons).Alejandro Garcia Anglada a publié un tutoriel pratique sur la façon de l'implémenter, Rahul Nanwani a écrit un article détaillé sur le chargement paresseux des images de premier plan et d'arrière-plan, et Google Fundamentals fournit également un tutoriel détaillé sur le chargement paresseux d'images et de vidéos avec Intersection Observer.
Vous souvenez-vous de la narration artistique de longues lectures avec des objets en mouvement et collants ? Vous pouvez également implémenter un scrollytelling performant avec Intersection Observer.
Vérifiez à nouveau ce que vous pourriez charger paresseux. Même les chaînes de traduction et les emoji à chargement paresseux pourraient aider. Ce faisant, Mobile Twitter a réussi à obtenir une exécution JavaScript 80 % plus rapide à partir du nouveau pipeline d'internationalisation.
Un petit avertissement cependant : il convient de noter que le chargement paresseux devrait être une exception plutôt que la règle. Il n'est probablement pas raisonnable de charger paresseux tout ce que vous voulez réellement que les gens voient rapidement, par exemple des images de page de produit, des images de héros ou un script requis pour que la navigation principale devienne interactive.
- Charger les images progressivement.
Vous pouvez même faire passer le chargement paresseux au niveau supérieur en ajoutant un chargement progressif des images à vos pages. Comme pour Facebook, Pinterest, Medium et Wolt, vous pouvez d'abord charger des images de faible qualité ou même floues, puis, au fur et à mesure que la page se charge, les remplacer par les versions de qualité complète en utilisant la technique BlurHash ou LQIP (Low Quality Image Placeholders) technique.Les opinions diffèrent si ces techniques améliorent ou non l'expérience utilisateur, mais cela améliore définitivement le temps de First Contentful Paint. Nous pouvons même l'automatiser en utilisant SQIP qui crée une version de faible qualité d'une image en tant qu'espace réservé SVG, ou des espaces réservés d'image dégradés avec des dégradés linéaires CSS.
Ces espaces réservés pourraient être intégrés dans HTML car ils se compriment naturellement bien avec les méthodes de compression de texte. Dans son article, Dean Hume a décrit comment cette technique peut être mise en œuvre à l'aide d'Intersection Observer.
Se retirer? Si le navigateur ne prend pas en charge l'observateur d'intersection, nous pouvons toujours charger paresseux un polyfill ou charger les images immédiatement. Et il y a même une bibliothèque pour ça.
Vous voulez devenir plus chic ? Vous pouvez tracer vos images et utiliser des formes et des bords primitifs pour créer un espace réservé SVG léger, le charger d'abord, puis passer de l'image vectorielle de l'espace réservé à l'image bitmap (chargée).
- Différez-vous le rendu avec
content-visibility
?
Pour une mise en page complexe avec de nombreux blocs de contenu, images et vidéos, le décodage des données et le rendu des pixels peuvent être une opération assez coûteuse, en particulier sur les appareils bas de gamme. Aveccontent-visibility: auto
, nous pouvons inviter le navigateur à ignorer la disposition des enfants lorsque le conteneur est en dehors de la fenêtre.Par exemple, vous pouvez ignorer le rendu du pied de page et des sections tardives lors du chargement initial :
footer { content-visibility: auto; contain-intrinsic-size: 1000px; /* 1000px is an estimated height for sections that are not rendered yet. */ }
Notez que la visibilité du contenu : auto ; se comporte comme un débordement : caché ; , mais vous pouvez résoudre ce problème en appliquant
padding-left
etpadding-right
au lieu de la valeur par défautmargin-left: auto;
,margin-right: auto;
et une largeur déclarée. Le rembourrage permet essentiellement aux éléments de déborder de la boîte de contenu et d'entrer dans la boîte de remplissage sans quitter le modèle de boîte dans son ensemble et être coupé.De plus, gardez à l'esprit que vous pouvez introduire du CLS lorsque le nouveau contenu est finalement rendu, c'est donc une bonne idée d'utiliser
contain-intrinsic-size
avec un espace réservé correctement dimensionné ( merci, Una ! ).Thijs Terluin a beaucoup plus de détails sur les deux propriétés et sur la façon dont
contain-intrinsic-size
est calculée par le navigateur, Malte Ubl montre comment vous pouvez le calculer et une brève vidéo explicative de Jake et Surma explique comment tout cela fonctionne.Et si vous avez besoin d'être un peu plus précis, avec CSS Containment, vous pouvez ignorer manuellement le travail de mise en page, de style et de peinture pour les descendants d'un nœud DOM si vous n'avez besoin que de la taille, de l'alignement ou des styles calculés sur d'autres éléments - ou si l'élément est actuellement hors toile.
- Reportez-vous le décodage avec
decoding="async"
?
Parfois, le contenu apparaît hors écran, mais nous voulons nous assurer qu'il est disponible lorsque les clients en ont besoin - idéalement, ne bloquez rien dans le chemin critique, mais décodez et restituez de manière asynchrone. Nous pouvons utiliserdecoding="async"
pour autoriser le navigateur à décoder l'image du thread principal, en évitant l'impact de l'utilisateur sur le temps CPU utilisé pour décoder l'image (via Malte Ubl) :<img decoding="async" … />
Alternativement, pour les images hors écran, nous pouvons d'abord afficher un espace réservé, et lorsque l'image est dans la fenêtre, en utilisant IntersectionObserver, déclencher un appel réseau pour que l'image soit téléchargée en arrière-plan. De plus, nous pouvons différer le rendu jusqu'au décodage avec img.decode() ou télécharger l'image si l'API Image Decode n'est pas disponible.
Lors du rendu de l'image, nous pouvons utiliser des animations de fondu enchaîné, par exemple. Katie Hempenius et Addy Osmani partagent plus d'informations dans leur conférence Speed at Scale: Web Performance Tips and Tricks from the Trenches.
- Générez-vous et servez-vous des CSS critiques ?
Pour s'assurer que les navigateurs commencent à afficher votre page le plus rapidement possible, il est devenu courant de collecter tous les CSS nécessaires pour commencer à afficher la première partie visible de la page (appelée « CSS critique » ou « CSS au-dessus de la ligne de flottaison ") et incluez-le en ligne dans le<head>
de la page, réduisant ainsi les allers-retours. En raison de la taille limitée des packages échangés pendant la phase de démarrage lent, votre budget pour le CSS critique est d'environ 14 Ko.Si vous allez au-delà, le navigateur aura besoin d'allers-retours supplémentaires pour récupérer plus de styles. CriticalCSS et Critical vous permettent de générer des CSS critiques pour chaque modèle que vous utilisez. D'après notre expérience, aucun système automatique n'a jamais été meilleur que la collecte manuelle de CSS critiques pour chaque modèle, et c'est en effet l'approche à laquelle nous sommes revenus récemment.
Vous pouvez ensuite intégrer le CSS critique et charger paresseux le reste avec le plugin Webpack de critters. Si possible, envisagez d'utiliser l'approche d'intégration conditionnelle utilisée par le groupe Filament ou convertissez le code en ligne en actifs statiques à la volée.
Si vous chargez actuellement votre CSS complet de manière asynchrone avec des bibliothèques telles que loadCSS, ce n'est pas vraiment nécessaire. Avec
media="print"
, vous pouvez inciter le navigateur à récupérer le CSS de manière asynchrone mais à l'appliquer à l'environnement de l'écran une fois qu'il se charge. ( merci Scott! )<!-- Via Scott Jehl. https://www.filamentgroup.com/lab/load-css-simpler/ --> <!-- Load CSS asynchronously, with low priority --> <link rel="stylesheet" href="full.css" media="print" onload="this.media='all'" />
Lors de la collecte de tous les CSS critiques pour chaque modèle, il est courant d'explorer uniquement la zone "au-dessus de la ligne de flottaison". Cependant, pour les mises en page complexes, il peut être judicieux d'inclure également les bases de la mise en page pour éviter des coûts de recalcul et de repeinture massifs , ce qui nuit à votre score Core Web Vitals.
Que se passe-t-il si un utilisateur obtient une URL qui renvoie directement au milieu de la page mais que le CSS n'a pas encore été téléchargé ? Dans ce cas, il est devenu courant de masquer le contenu non critique, par exemple avec une
opacity: 0;
en CSS intégré etopacity: 1
dans le fichier CSS complet, et affichez-le lorsque le CSS est disponible. Il présente cependant un inconvénient majeur , car les utilisateurs ayant des connexions lentes pourraient ne jamais être en mesure de lire le contenu de la page. C'est pourquoi il est préférable de toujours garder le contenu visible, même s'il n'est peut-être pas stylisé correctement.Mettre le CSS critique (et d'autres actifs importants) dans un fichier séparé sur le domaine racine présente des avantages, parfois même plus que l'inlining, en raison de la mise en cache. Chrome ouvre de manière spéculative une deuxième connexion HTTP au domaine racine lors de la demande de la page, ce qui supprime le besoin d'une connexion TCP pour récupérer ce CSS. Cela signifie que vous pouvez créer un ensemble de fichiers -CSS critiques (par exemple, Critical -Homepage.css , Critical-Product-Page.Css, etc.) et les servir à partir de votre racine, sans avoir à les intégrer. ( merci Philippe! )
Un mot d'avertissement : avec HTTP/2, les CSS critiques pourraient être stockées dans un fichier CSS séparé et livrées via une poussée du serveur sans gonfler le HTML. Le hic, c'est que la poussée de serveur était gênante avec de nombreux pièges et conditions de concurrence entre les navigateurs. Il n'a jamais été pris en charge de manière cohérente et présentait des problèmes de mise en cache (voir la diapositive 114 et suivantes de la présentation de Hooman Beheshti).
L'effet pourrait, en fait, être négatif et gonfler les mémoires tampons du réseau, empêchant la livraison des images authentiques du document. Il n'est donc pas très surprenant que pour le moment, Chrome envisage de supprimer la prise en charge de Server Push.
- Essayez de regrouper vos règles CSS.
Nous nous sommes habitués au CSS critique, mais il y a quelques optimisations qui pourraient aller au-delà. Harry Roberts a mené une recherche remarquable avec des résultats assez surprenants. Par exemple, il peut être judicieux de diviser le fichier CSS principal en ses requêtes multimédia individuelles. De cette façon, le navigateur récupérera les CSS critiques avec une priorité élevée et tout le reste avec une faible priorité - complètement hors du chemin critique.Évitez également de placer
<link rel="stylesheet" />
avant les extraits deasync
. Si les scripts ne dépendent pas des feuilles de style, pensez à placer les scripts de blocage au-dessus des styles de blocage. Si c'est le cas, divisez ce JavaScript en deux et chargez-le de chaque côté de votre CSS.Scott Jehl a résolu un autre problème intéressant en mettant en cache un fichier CSS intégré avec un service worker, un problème courant si vous utilisez des CSS critiques. Fondamentalement, nous ajoutons un attribut ID sur l'élément de
style
afin qu'il soit facile de le trouver en utilisant JavaScript, puis un petit morceau de JavaScript trouve que CSS et utilise l'API Cache pour le stocker dans un cache de navigateur local (avec un type de contenu detext/css
) à utiliser sur les pages suivantes. Pour éviter l'intégration sur les pages suivantes et référencer à la place les actifs mis en cache en externe, nous installons ensuite un cookie lors de la première visite sur un site. Voila !Il convient de noter que le style dynamique peut également être coûteux, mais généralement uniquement dans les cas où vous comptez sur des centaines de composants composés rendus simultanément. Donc, si vous utilisez CSS-in-JS, assurez-vous que votre bibliothèque CSS-in-JS optimise l'exécution lorsque votre CSS n'a aucune dépendance sur le thème ou les accessoires, et ne sur-composez pas de composants stylés . Aggelos Arvanitakis partage plus d'informations sur les coûts de performance de CSS-in-JS.
- Diffusez-vous les réponses ?
Souvent oubliés et négligés, les flux fournissent une interface pour lire ou écrire des blocs de données asynchrones, dont seul un sous-ensemble peut être disponible en mémoire à un moment donné. Fondamentalement, ils permettent à la page qui a fait la demande d'origine de commencer à travailler avec la réponse dès que le premier bloc de données est disponible, et utilisent des analyseurs optimisés pour le streaming pour afficher progressivement le contenu.Nous pourrions créer un flux à partir de plusieurs sources. Par exemple, au lieu de servir un shell d'interface utilisateur vide et de laisser JavaScript le remplir, vous pouvez laisser le service worker construire un flux où le shell provient d'un cache, mais le corps provient du réseau. Comme l'a noté Jeff Posnick, si votre application Web est alimentée par un CMS qui rend le HTML par le serveur en assemblant des modèles partiels, ce modèle se traduit directement par l'utilisation de réponses en continu, avec la logique de modélisation répliquée dans le service worker au lieu de votre serveur. L'article The Year of Web Streams de Jake Archibald montre comment vous pouvez le construire. L'amélioration des performances est assez notable.
L'un des avantages importants de la diffusion en continu de l'intégralité de la réponse HTML est que le HTML rendu lors de la demande de navigation initiale peut tirer pleinement parti de l'analyseur HTML en continu du navigateur. Les morceaux de code HTML insérés dans un document après le chargement de la page (comme c'est souvent le cas avec le contenu rempli via JavaScript) ne peuvent pas tirer parti de cette optimisation.
Prise en charge du navigateur ? Toujours en train d'y arriver avec une prise en charge partielle de Chrome, Firefox, Safari et Edge prenant en charge l'API et les Service Workers pris en charge dans tous les navigateurs modernes. Et si vous vous sentez à nouveau aventureux, vous pouvez vérifier une implémentation expérimentale des requêtes de streaming, qui vous permet de commencer à envoyer la requête tout en générant le corps. Disponible dans Chrome 85.
- Envisagez de rendre vos composants compatibles avec la connexion.
Les données peuvent être coûteuses et avec une charge utile croissante, nous devons respecter les utilisateurs qui choisissent d'opter pour des économies de données lorsqu'ils accèdent à nos sites ou applications. L'en-tête de demande d'indice client Save-Data nous permet de personnaliser l'application et la charge utile pour les utilisateurs soumis à des contraintes de coût et de performances.En fait, vous pouvez réécrire les demandes d'images à haute résolution en images à faible résolution, supprimer les polices Web, les effets de parallaxe fantaisistes, prévisualiser les vignettes et le défilement infini, désactiver la lecture automatique des vidéos, les poussées de serveur, réduire le nombre d'éléments affichés et réduire la qualité de l'image, ou même changer la façon dont vous fournissez le balisage. Tim Vereecke a publié un article très détaillé sur les stratégies de data-s(h)aver proposant de nombreuses options de sauvegarde de données.
Qui utilise
save-data
, vous vous demandez peut-être ? 18 % des utilisateurs mondiaux d'Android Chrome ont activé le mode Lite (avecSave-Data
activé), et le nombre est susceptible d'être plus élevé. Selon les recherches de Simon Hearne, le taux d'adhésion est le plus élevé sur les appareils moins chers, mais il existe de nombreuses valeurs aberrantes. Par exemple : les utilisateurs au Canada ont un taux d'adhésion de plus de 34 % (par rapport à environ 7 % aux États-Unis) et les utilisateurs du dernier produit phare de Samsung ont un taux d'adhésion de près de 18 % dans le monde.Avec le mode
Save-Data
activé, Chrome Mobile fournira une expérience optimisée, c'est-à-dire une expérience Web proxy avec des scripts différés ,font-display: swap
et chargement différé forcé. Il est simplement plus judicieux de créer l'expérience par vous-même plutôt que de compter sur le navigateur pour effectuer ces optimisations.L'en-tête est actuellement pris en charge uniquement dans Chromium, sur la version Android de Chrome ou via l'extension Data Saver sur un appareil de bureau. Enfin, vous pouvez également utiliser l'API Network Information pour fournir des modules JavaScript coûteux, des images et des vidéos haute résolution en fonction du type de réseau. L'API d'informations réseau et plus particulièrement
navigator.connection.effectiveType
utilisent les valeursRTT
,downlink
,effectiveType
(et quelques autres) pour fournir une représentation de la connexion et des données que les utilisateurs peuvent gérer.Dans ce contexte, Max Bock parle de composants sensibles à la connexion et Addy Osmani parle de service de module adaptatif. Par exemple, avec React, nous pourrions écrire un composant qui s'affiche différemment pour différents types de connexion. Comme Max l'a suggéré, un composant
<Media />
dans un article de presse peut afficher :-
Offline
: un espace réservé avec un textealt
, - Mode
2G
/save-data
: une image basse résolution, -
3G
sur écran non Retina : une image en moyenne résolution, -
3G
sur écrans Retina : image Retina haute résolution, -
4G
: une vidéo HD.
Dean Hume fournit une implémentation pratique d'une logique similaire à l'aide d'un service worker. Pour une vidéo, nous pourrions afficher une affiche vidéo par défaut, puis afficher l'icône "Play" ainsi que le shell du lecteur vidéo, les métadonnées de la vidéo, etc. sur de meilleures connexions. En guise de solution de rechange pour les navigateurs non compatibles, nous pourrions écouter l'événement
canplaythrough
et utiliserPromise.race()
pour expirer le chargement de la source si l'événementcanplaythrough
ne se déclenche pas dans les 2 secondes.Si vous souhaitez approfondir un peu, voici quelques ressources pour commencer :
- Addy Osmani montre comment implémenter le service adaptatif dans React.
- React Adaptive Loading Hooks & Utilities fournit des extraits de code pour React,
- Netanel Basel explore les composants compatibles avec les connexions dans Angular,
- Theodore Vorilas explique comment fonctionne le service de composants adaptatifs à l'aide de l'API d'informations réseau dans Vue.
- Umar Hansa montre comment télécharger/exécuter sélectivement du JavaScript coûteux.
-
- Envisagez de rendre vos composants compatibles avec la mémoire de l'appareil.
La connexion réseau ne nous donne cependant qu'une seule perspective dans le contexte de l'utilisateur. Pour aller plus loin, vous pouvez également ajuster dynamiquement les ressources en fonction de la mémoire disponible de l'appareil, avec l'API Device Memory.navigator.deviceMemory
renvoie la quantité de RAM dont dispose l'appareil en gigaoctets, arrondie à la puissance de deux la plus proche. L'API comporte également un en-tête Client Hints,Device-Memory
, qui signale la même valeur.Bonus : Umar Hansa montre comment reporter des scripts coûteux avec des importations dynamiques pour modifier l'expérience en fonction de la mémoire de l'appareil, de la connectivité réseau et de la simultanéité matérielle.
- Réchauffez la connexion pour accélérer la livraison.
Utilisez des conseils de ressources pour gagner du temps surdns-prefetch
(qui effectue une recherche DNS en arrière-plan),preconnect
(qui demande au navigateur de démarrer la poignée de main de connexion (DNS, TCP, TLS) en arrière-plan),prefetch
(qui demande au navigateur pour demander une ressource) etpreload
(qui précharge les ressources sans les exécuter, entre autres). Bien pris en charge dans les navigateurs modernes, avec un support bientôt disponible sur Firefox.Vous vous souvenez
prerender
? L'indice de ressource utilisé pour inviter le navigateur à créer la page entière en arrière-plan pour la prochaine navigation. Les problèmes de mise en œuvre étaient assez problématiques, allant d'une empreinte mémoire et d'une utilisation de la bande passante énormes à de multiples hits d'analyse enregistrés et impressions publicitaires.Sans surprise, il était obsolète, mais l'équipe Chrome l'a ramené en tant que mécanisme NoState Prefetch. En fait, Chrome traite l'indice de
prerender
comme un NoState Prefetch à la place, nous pouvons donc toujours l'utiliser aujourd'hui. Comme l'explique Katie Hempenius dans cet article, "comme le pré-rendu, NoState Prefetch récupère les ressources à l'avance ; mais contrairement au pré-rendu, il n'exécute pas JavaScript ni ne rend aucune partie de la page à l'avance".NoState Prefetch n'utilise que ~ 45 Mo de mémoire et les sous-ressources extraites seront extraites avec une priorité réseau
IDLE
. Depuis Chrome 69, NoState Prefetch ajoute l'en-tête Purpose: Prefetch à toutes les requêtes afin de les distinguer de la navigation normale.Faites également attention aux alternatives de pré-rendu et aux portails, un nouvel effort vers un pré-rendu soucieux de la confidentialité, qui fournira un
preview
en médaillon du contenu pour des navigations transparentes.L'utilisation d'indicateurs de ressources est probablement le moyen le plus simple d'améliorer les performances , et cela fonctionne bien. Quand utiliser quoi ? Comme Addy Osmani l'a expliqué, il est raisonnable de précharger les ressources dont nous savons qu'elles seront très probablement utilisées sur la page actuelle et pour les navigations futures à travers plusieurs limites de navigation, par exemple les bundles Webpack nécessaires pour les pages que l'utilisateur n'a pas encore visitées.
L'article d'Addy sur "Loading Priorities in Chrome" montre comment Chrome interprète exactement les indices de ressources, donc une fois que vous avez décidé quels actifs sont critiques pour le rendu, vous pouvez leur attribuer une priorité élevée. Pour voir comment vos demandes sont classées par ordre de priorité, vous pouvez activer une colonne "priorité" dans le tableau des demandes réseau de Chrome DevTools (ainsi que Safari).
La plupart du temps ces jours-ci, nous utiliserons au moins
preconnect
etdns-prefetch
, et nous serons prudents avec l'utilisation deprefetch
,preload
etprerender
. Notez que même avecpreconnect
etdns-prefetch
, le navigateur a une limite sur le nombre d'hôtes qu'il recherchera/se connectera en parallèle, il est donc prudent de les ordonner en fonction de la priorité ( merci Philip Tellis ! ).Étant donné que les polices sont généralement des actifs importants sur une page, il est parfois judicieux de demander au navigateur de télécharger les polices critiques avec
preload
. Cependant, vérifiez si cela améliore réellement les performances, car il existe un casse-tête de priorités lors du préchargement des polices : comme lepreload
est considéré comme très important, il peut dépasser des ressources encore plus critiques comme le CSS critique. ( merci Barry! )<!-- Loading two rendering-critical fonts, but not all their weights. --> <!-- crossorigin="anonymous" is required due to CORS. Without it, preloaded fonts will be ignored. https://github.com/w3c/preload/issues/32 via https://twitter.com/iamakulov/status/1275790151642423303 --> <link rel="preload" as="font" href="Elena-Regular.woff2" type="font/woff2" crossorigin="anonymous" media="only screen and (min-width: 48rem)" /> <link rel="preload" as="font" href="Mija-Bold.woff2" type="font/woff2" crossorigin="anonymous" media="only screen and (min-width: 48rem)" />
<!-- Loading two rendering-critical fonts, but not all their weights. --> <!-- crossorigin="anonymous" is required due to CORS. Without it, preloaded fonts will be ignored. https://github.com/w3c/preload/issues/32 via https://twitter.com/iamakulov/status/1275790151642423303 --> <link rel="preload" as="font" href="Elena-Regular.woff2" type="font/woff2" crossorigin="anonymous" media="only screen and (min-width: 48rem)" /> <link rel="preload" as="font" href="Mija-Bold.woff2" type="font/woff2" crossorigin="anonymous" media="only screen and (min-width: 48rem)" />
Étant donné que
<link rel="preload">
accepte un attributmedia
, vous pouvez choisir de télécharger sélectivement des ressources en fonction des règles de requête@media
, comme indiqué ci-dessus.De plus, nous pouvons utiliser les attributs
imagesrcset
etimagesizes
pour précharger plus rapidement les images de héros découvertes tardivement, ou toute image chargée via JavaScript, par exemple des affiches de films :<!-- Addy Osmani. https://addyosmani.com/blog/preload-hero-images/ --> <link rel="preload" as="image" href="poster.jpg" image image>
<!-- Addy Osmani. https://addyosmani.com/blog/preload-hero-images/ --> <link rel="preload" as="image" href="poster.jpg" image image>
Nous pouvons également précharger le JSON en tant que fetch , afin qu'il soit découvert avant que JavaScript ne le demande :
<!-- Addy Osmani. https://addyosmani.com/blog/preload-hero-images/ --> <link rel="preload" as="fetch" href="foo.com/api/movies.json" crossorigin>
Nous pourrions également charger dynamiquement JavaScript, efficacement pour une exécution paresseuse du script.
/* Adding a preload hint to the head */ var preload = document.createElement("link"); link.href = "myscript.js"; link.rel = "preload"; link.as = "script"; document.head.appendChild(link); /* Injecting a script when we want it to execute */ var script = document.createElement("script"); script.src = "myscript.js"; document.body.appendChild(script);
Quelques pièges à garder à l'esprit : le
preload
est utile pour rapprocher l'heure de début du téléchargement d'un actif de la demande initiale, mais les actifs préchargés atterrissent dans le cache mémoire qui est lié à la page faisant la demande. lepreload
fonctionne bien avec le cache HTTP : une requête réseau n'est jamais envoyée si l'élément est déjà présent dans le cache HTTP.Par conséquent, il est utile pour les ressources découvertes tardivement, les images de héros chargées via
background-image
, l'intégration de CSS critiques (ou JavaScript) et le préchargement du reste du CSS (ou JavaScript).Une balise de
preload
peut initier un préchargement uniquement après que le navigateur a reçu le code HTML du serveur et que l'analyseur d'anticipation a trouvé la balise depreload
. Le préchargement via l'en-tête HTTP pourrait être un peu plus rapide car nous n'attendons pas que le navigateur analyse le HTML pour lancer la requête (c'est cependant débattu).Les premiers conseils aideront encore plus, permettant au préchargement de démarrer avant même que les en-têtes de réponse pour le HTML ne soient envoyés (sur la feuille de route dans Chromium, Firefox). De plus, les conseils de priorité nous aideront à indiquer les priorités de chargement des scripts.
Attention : si vous utilisez
preload
,as
doit être défini ou rien ne se charge, de plus les polices préchargées sans l'attributcrossorigin
seront récupérées deux fois. Si vous utilisezprefetch
, méfiez-vous des problèmes d'en-têteAge
dans Firefox.
- Utilisez les techniciens de service pour la mise en cache et les secours réseau.
Aucune optimisation des performances sur un réseau ne peut être plus rapide qu'un cache stocké localement sur la machine d'un utilisateur (il existe cependant des exceptions). Si votre site Web s'exécute sur HTTPS, nous pouvons mettre en cache des actifs statiques dans un cache de service worker et stocker des secours hors ligne (ou même des pages hors ligne) et les récupérer depuis la machine de l'utilisateur, plutôt que d'aller sur le réseau.Comme suggéré par Phil Walton, avec les techniciens de service, nous pouvons envoyer des charges utiles HTML plus petites en générant nos réponses par programmation. Un travailleur de service peut demander au serveur le strict minimum de données dont il a besoin (par exemple, un contenu HTML partiel, un fichier Markdown, des données JSON, etc.), puis il peut transformer par programmation ces données en un document HTML complet. Ainsi, une fois qu'un utilisateur visite un site et que le service worker est installé, l'utilisateur ne demandera plus jamais une page HTML complète. L'impact sur les performances peut être assez impressionnant.
Prise en charge du navigateur ? Les services workers sont largement pris en charge et la solution de repli est de toute façon le réseau. Est-ce que ça aide à booster les performances ? Oh oui, c'est le cas. Et cela s'améliore, par exemple avec Background Fetch permettant également les téléchargements/téléchargements en arrière-plan via un service worker.
Il existe un certain nombre de cas d'utilisation pour un service worker. Par exemple, vous pouvez implémenter la fonctionnalité "Enregistrer pour hors ligne", gérer les images cassées, introduire la messagerie entre les onglets ou fournir différentes stratégies de mise en cache en fonction des types de requêtes. En général, une stratégie fiable courante consiste à stocker le shell de l'application dans le cache du service worker avec quelques pages critiques, telles que la page hors ligne, la page d'accueil et tout ce qui pourrait être important dans votre cas.
Il y a cependant quelques pièges à garder à l'esprit. Avec un agent de service en place, nous devons nous méfier des demandes de plage dans Safari (si vous utilisez Workbox pour un agent de service, il dispose d'un module de demande de plage). Si jamais vous êtes tombé sur
DOMException: Quota exceeded.
erreur dans la console du navigateur, puis consultez l'article de Gerardo Lorsque 7 Ko équivaut à 7 Mo.Comme l'écrit Gerardo, "Si vous créez une application Web progressive et que vous rencontrez un stockage de cache gonflé lorsque votre service worker met en cache des actifs statiques servis à partir de CDN, assurez-vous que l'en-tête de réponse CORS approprié existe pour les ressources d'origine croisée, vous ne mettez pas en cache les réponses opaques avec votre travailleur de service involontairement, vous optez pour les ressources d'image cross-origin en mode CORS en ajoutant l'attribut
crossorigin
à la<img>
.Il existe de nombreuses ressources intéressantes pour démarrer avec les techniciens de service :
- Service Worker Mindset, qui vous aide à comprendre comment les travailleurs de service travaillent dans les coulisses et les choses à comprendre lors de la création d'un.
- Chris Ferdinandi propose une excellente série d'articles sur les techniciens de service, expliquant comment créer des applications hors ligne et couvrant une variété de scénarios, de l'enregistrement hors ligne des pages récemment consultées à la définition d'une date d'expiration pour les éléments d'un cache de technicien de service.
- Pièges et meilleures pratiques des techniciens de service, avec quelques conseils sur la portée, retarder l'enregistrement d'un technicien de service et la mise en cache du technicien de service.
- Super série d'Ire Aderinokun sur "Offline First" avec Service Worker, avec une stratégie sur le precaching du shell de l'app.
- Service Worker : Une introduction avec des conseils pratiques sur la façon d'utiliser le service worker pour des expériences hors ligne riches, des synchronisations périodiques en arrière-plan et des notifications push.
- Il vaut toujours la peine de se référer au bon vieux livre de recettes hors ligne de Jake Archibald avec un certain nombre de recettes sur la façon de préparer votre propre travailleur de service.
- Workbox est un ensemble de bibliothèques de service worker spécialement conçues pour créer des applications Web progressives.
- Exécutez-vous des serveurs de travail sur le CDN/Edge, par exemple pour des tests A/B ?
À ce stade, nous sommes assez habitués à exécuter des service workers sur le client, mais avec les CDN qui les implémentent sur le serveur, nous pourrions également les utiliser pour ajuster les performances en périphérie.Par exemple, dans les tests A/B, lorsque HTML doit faire varier son contenu pour différents utilisateurs, nous pouvons utiliser Service Workers sur les serveurs CDN pour gérer la logique. Nous pourrions également diffuser la réécriture HTML pour accélérer les sites qui utilisent Google Fonts.
- Optimisez les performances de rendu.
Chaque fois que l'application est lente, cela se remarque tout de suite. Nous devons donc nous assurer qu'il n'y a pas de décalage lors du défilement de la page ou lorsqu'un élément est animé, et que vous atteignez systématiquement 60 images par seconde. Si ce n'est pas possible, alors au moins rendre les images par seconde cohérentes est préférable à une plage mixte de 60 à 15. Utilisez CSS'will-change
pour informer le navigateur des éléments et des propriétés qui vont changer.Chaque fois que vous rencontrez des problèmes, déboguez les repeints inutiles dans DevTools :
- Mesurez les performances de rendu à l'exécution. Découvrez quelques conseils utiles sur la façon de lui donner un sens.
- Pour commencer, consultez le cours Udacity gratuit de Paul Lewis sur l'optimisation du rendu du navigateur et l'article de Georgy Marchuk sur la peinture du navigateur et les considérations pour les performances Web.
- Activez Paint Flashing dans "Plus d'outils → Rendu → Paint Flashing" dans Firefox DevTools.
- Dans React DevTools, cochez "Mises à jour en surbrillance" et activez "Enregistrer pourquoi chaque composant est rendu",
- Vous pouvez également utiliser Why Did You Render, ainsi, lorsqu'un composant est rendu à nouveau, un flash vous avertit du changement.
Utilisez-vous une disposition de maçonnerie ? Gardez à l'esprit que vous pourrez peut-être créer une mise en page Masonry avec une grille CSS seule, très bientôt.
Si vous souhaitez approfondir le sujet, Nolan Lawson a partagé des astuces pour mesurer avec précision les performances de mise en page dans son article, et Jason Miller a également suggéré des techniques alternatives. Nous avons également un petit article de Sergey Chikuyonok sur la façon d'obtenir une bonne animation GPU.
Remarque : les modifications apportées aux couches composées par GPU sont les moins coûteuses, donc si vous pouvez vous en tirer en ne déclenchant que la composition via
opacity
ettransform
, vous serez sur la bonne voie. Anna Migas a également fourni de nombreux conseils pratiques dans son exposé sur le débogage des performances de rendu de l'interface utilisateur. Et pour comprendre comment déboguer les performances de peinture dans DevTools, consultez la vidéo d'audit des performances de peinture d'Umar. - Avez-vous optimisé les performances perçues ?
Bien que la séquence d'affichage des composants sur la page et la stratégie de diffusion des actifs dans le navigateur soient importantes, nous ne devons pas non plus sous-estimer le rôle des performances perçues. Le concept traite des aspects psychologiques de l'attente, en gardant essentiellement les clients occupés ou engagés pendant que quelque chose d'autre se passe. C'est là que la gestion de la perception, le démarrage préemptif, l'achèvement précoce et la gestion de la tolérance entrent en jeu.Qu'est-ce que tout cela veut dire? Lors du chargement des actifs, nous pouvons essayer d'avoir toujours une longueur d'avance sur le client, afin que l'expérience soit rapide alors qu'il se passe beaucoup de choses en arrière-plan. Pour garder le client engagé, nous pouvons tester des écrans squelettes (démo d'implémentation) au lieu de charger des indicateurs, ajouter des transitions/animations et essentiellement tromper l'UX lorsqu'il n'y a plus rien à optimiser.
Dans leur étude de cas sur The Art of UI Skeletons, Kumar McMillan partage quelques idées et techniques sur la façon de simuler des listes dynamiques, du texte et l'écran final, ainsi que sur la façon d'envisager la pensée squelette avec React.
Attention cependant : les écrans squelettes doivent être testés avant d'être déployés, car certains tests ont montré que les écrans squelettes peuvent être les pires selon toutes les métriques.
- Empêchez-vous les changements de mise en page et les repeints ?
Dans le domaine des performances perçues, l'une des expériences les plus perturbatrices est probablement le changement de mise en page , ou les refusions , causées par des images et des vidéos redimensionnées, des polices Web, des publicités injectées ou des scripts découverts tardivement qui remplissent les composants avec du contenu réel. Par conséquent, un client peut commencer à lire un article juste pour être interrompu par un saut de mise en page au-dessus de la zone de lecture. L'expérience est souvent abrupte et assez déroutante : et c'est probablement un cas de priorités de chargement qu'il faut revoir.La communauté a développé quelques techniques et solutions de contournement pour éviter les refusions. En général, il est conseillé d' éviter d'insérer un nouveau contenu au-dessus du contenu existant , sauf si cela se produit en réponse à une interaction de l'utilisateur. Définissez toujours les attributs de largeur et de hauteur sur les images, afin que les navigateurs modernes allouent la boîte et réservent l'espace par défaut (Firefox, Chrome).
Pour les images ou les vidéos, nous pouvons utiliser un espace réservé SVG pour réserver la zone d'affichage dans laquelle le média apparaîtra. Cela signifie que la zone sera réservée correctement lorsque vous devrez également conserver son rapport d'aspect. Nous pouvons également utiliser des espaces réservés ou des images de secours pour les publicités et le contenu dynamique, ainsi que des emplacements de mise en page pré-alloués.
Au lieu de charger des images paresseuses avec des scripts externes, envisagez d'utiliser le chargement paresseux natif ou le chargement paresseux hybride lorsque nous chargeons un script externe uniquement si le chargement paresseux natif n'est pas pris en charge.
Comme mentionné ci-dessus, regroupez toujours les repeints de polices Web et passez de toutes les polices de secours à toutes les polices Web à la fois - assurez-vous simplement que ce changement n'est pas trop brusque, en ajustant la hauteur de ligne et l'espacement entre les polices avec font-style-matcher .
Pour remplacer les métriques de police pour une police de secours afin d'émuler une police Web, nous pouvons utiliser des descripteurs @font-face pour remplacer les métriques de police (démo, activé dans Chrome 87). (Notez que les ajustements sont compliqués avec des piles de polices compliquées.)
Pour les CSS tardifs, nous pouvons nous assurer que le CSS critique pour la mise en page est intégré dans l'en-tête de chaque modèle. Encore plus loin que cela : pour les longues pages, lorsque la barre de défilement verticale est ajoutée, elle décale le contenu principal de 16 px vers la gauche. Pour afficher une barre de défilement plus tôt, nous pouvons ajouter
overflow-y: scroll
onhtml
pour appliquer une barre de défilement au premier dessin. Ce dernier est utile car les barres de défilement peuvent provoquer des changements de mise en page non triviaux en raison du contenu au-dessus du pli qui se redistribue lorsque la largeur change. Cela devrait surtout se produire sur des plates-formes avec des barres de défilement non superposées comme Windows. Mais : pausesposition: sticky
car ces éléments ne défileront jamais hors du conteneur.Si vous traitez des en-têtes qui deviennent fixes ou collants positionnés en haut de la page lors du défilement, réservez de l'espace pour l'en-tête lorsqu'il devient piné, par exemple avec un élément d'espace réservé ou une
margin-top
sur le contenu. Une exception devrait être les bannières de consentement aux cookies qui ne devraient pas avoir d'impact sur CLS, mais parfois elles en ont : cela dépend de la mise en œuvre. Il y a quelques stratégies et plats à emporter intéressants dans ce fil Twitter.Pour un composant d'onglet pouvant inclure différentes quantités de texte, vous pouvez empêcher les changements de mise en page avec des piles de grilles CSS. En plaçant le contenu de chaque onglet dans la même zone de grille et en masquant l'un d'eux à la fois, nous pouvons nous assurer que le conteneur prend toujours la hauteur de l'élément le plus grand, de sorte qu'aucun changement de disposition ne se produira.
Ah, et bien sûr, le défilement infini et "Charger plus" peuvent également entraîner des changements de mise en page s'il y a du contenu sous la liste (par exemple, le pied de page). Pour améliorer CLS, réservez suffisamment d'espace pour le contenu qui serait chargé avant que l'utilisateur ne fasse défiler cette partie de la page, supprimez le pied de page ou tout élément DOM au bas de la page qui pourrait être poussé vers le bas par le chargement du contenu. En outre, prérécupérez les données et les images pour le contenu en dessous de la ligne de flottaison afin qu'au moment où un utilisateur défile aussi loin, il soit déjà là. Vous pouvez également utiliser des bibliothèques de virtualisation de liste comme react-window pour optimiser de longues listes ( merci, Addy Osmani ! ).
Pour vous assurer que l'impact des refusions est contenu, mesurez la stabilité de la mise en page avec l'API Layout Instability. Avec lui, vous pouvez calculer le score Cumulative Layout Shift ( CLS ) et l'inclure comme exigence dans vos tests, de sorte qu'à chaque fois qu'une régression apparaît, vous pouvez la suivre et la corriger.
Pour calculer le score de décalage de mise en page, le navigateur examine la taille de la fenêtre et le mouvement des éléments instables dans la fenêtre entre deux images rendues. Idéalement, le score serait proche de
0
. Il existe un excellent guide de Milica Mihajlija et Philip Walton sur ce qu'est CLS et comment le mesurer. C'est un bon point de départ pour mesurer et maintenir les performances perçues et éviter les interruptions, en particulier pour les tâches critiques de l'entreprise.Petite astuce : pour découvrir ce qui a provoqué un changement de disposition dans DevTools, vous pouvez explorer les changements de disposition sous "Expérience" dans le panneau de performances.
Bonus : si vous souhaitez réduire les reflows et les repaints, consultez le guide de Charis Theodoulou sur Minimizing DOM Reflow/Layout Thrashing et la liste de Paul Irish sur What forces layout / reflow ainsi que CSSTriggers.com, une table de référence sur les propriétés CSS qui déclenchent layout, paint et composition.
Table des matières
- Se préparer : planification et métriques
- Fixer des objectifs réalistes
- Définir l'environnement
- Optimisations des actifs
- Construire des optimisations
- Optimisations de livraison
- Mise en réseau, HTTP/2, HTTP/3
- Test et surveillance
- Victoires rapides
- Tout sur une seule page
- Télécharger la liste de contrôle (PDF, pages Apple, MS Word)
- Abonnez-vous à notre newsletter par e-mail pour ne pas manquer les prochains guides.