Home Blogs Optimisation de votre site Web pour Lighthouse v6.0
Applications

Optimisation de votre site Web pour Lighthouse v6.0

About The Author

Outline

Layer0 est un contributeur majeur au framework PWA eCommerce open source, React Storefront. Plus tôt cette année, nous avons contribué à de nombreuses nouvelles fonctionnalités et optimisations comme React Storefront v7, Deux des plus importants ont été le passage à Next.js et la suppression de plusieurs dépendances clés (par exemple MobX) en faveur des nouvelles capacités intégrées de React pour gérer l’état, telles que le hook useState et l’API de contexte. Cela a entraîné une réduction de moitié de la taille du bundle du navigateur.

Ce fut un gain appréciable à l’époque et a aidé à faire grimper les scores de performance de Lighthouse (v5.7) pour les applications React Storefront typiques des années 80 aux années 90, mesurés par PageSpeed Insights (PSI). Pour le contexte, un score de 83+ a surpassé 99% des 500 meilleurs sites de commerce électronique sur Lighthouse v5.7. Nous ne nous rendions pas compte à quel point la réduction du bundle se révélerait essentielle dans les mois à venir, lorsque Lighthouse v6.0 tomberait comme une bombe et oblitérerait les scores de performance de chacun.

Découvrez comment la distribution des scores Lighthouse mesurés sur PSI pour les principaux sites de commerce électronique a changé lorsque v6.0 a chuté:

Dans cet article, nous partageons comment nous avons amélioré les scores Lighthouse v6.0 pour React Storefront, mais les techniques peuvent être appliquées à d’autres frameworks.

De plus, il est important de noter que Google a annoncé le 28 mai 2020, les métriques spécifiques qu’ils utiliseront pour classer les sites en 2021. Le score de performance Lighthouse ne sera pas utilisé, bien que certains éléments utilisés pour déterminer ce score le seront, et même dans ce cas, ils ne seront pas mesurés à l’aide d’un test synthétique tel que Lighthouse, mais plutôt de données de terrain réelles issues du Chrome User Experience Report (Crux).

Les nouvelles métriques dans Lighthouse 6,0 : TBT, LCP & CLS

Lighthouse v6.0 introduit plusieurs nouvelles mesures de vitesse perceptive et reformule la façon dont les mesures globales affectent le score de performance Lighthouse d’une page.

Phare 5,7 Poids Phare 6,0 Poids
Première peinture complète (FCP) 20% Première peinture complète (FCP) 15%
Indice de vitesse (si) 27% Indice de vitesse (si) 15%
Première peinture significative (PMF) 7% Peinture la plus épaisse (LCP) 25%
Premier CPU inactif (FCI) 13% Temps total de blocage (pas à pas) 15%
Délai d'interactivité (TTI) 33% Délai d'interactivité (TTI) 15%
- - Décalage cumulé de la mise en page (CLS) 5%

Temps total de blocage (pas à pas)

Le temps de blocage total est une nouvelle mesure incluse dans Lighthouse v6.0 qui mesure le temps pendant lequel l’analyse et l’exécution de JavaScript bloquent le thread principal pendant le chargement de la page. Cette métrique traite très sévèrement les spas/PWA modernes et lourds en JavaScript. Même avec la réduction de 50 % de la taille des paquets de React Storefront 7, nous avons constaté une baisse de 20 à 30 points du score de performance Lighthouse v6.0 pour les pages de produits, en grande partie due à l’inclusion du TBT comme métrique qui influence 25 % du score de performance global.

Si vous utilisez un framework isomorphe, comme Next.js, qui prend en charge le rendu côté serveur, le TBT est principalement déterminé par la taille du bundle et le temps d’hydratation. En termes simples, la seule façon d’améliorer le TBT est de supprimer les dépendances, d’optimiser vos composants ou de simplifier votre site en utilisant moins de composants.

Peinture la plus complète (LCP)

LCP est une nouvelle métrique qui a un poids de 25% sur le score global Lighthouse v6.0. LCP vise à quantifier la façon dont l’utilisateur perçoit les performances de chargement initial de la page en observant le temps nécessaire à l’élément le plus content pour terminer la peinture. Pour la plupart des sites, en particulier dans les sites de commerce électronique, le plus grand élément contentant est l’image de héros. Dans le cas des pages produit dans les applications React Storefront, l’élément le plus contentant est l’image principale du produit. Si vous n’êtes pas sûr de quel élément il s’agit sur votre site, PSI vous le dira:

Pour optimiser pour LCP, vous devez vous assurer que l’image se charge le plus rapidement possible.

Décalage cumulé de la mise en page (CLS)

Le décalage cumulé de la mise en page mesure le décalage de la mise en page pendant le chargement initial de la page. Le changement de disposition est le plus souvent causé par les images, qui ont tendance à pousser les éléments autour d’elles lorsqu’elles se redimensionnent pour s’adapter à l’image une fois les données téléchargées à partir du réseau. Le décalage de la disposition peut souvent être complètement éliminé en réservant de l’espace pour chaque image avant son chargement. Heureusement, le composant image de React Storefront le fait déjà, de sorte que l’application de démarrage React Storefront affiche un score CLS parfait de 0 prêt à l’emploi.

Il convient de noter que d’autres coupables communs de CLS pauvres sont les bannières et les popups qui apparaissent après que la page est initialement peinte. Les utilisateurs les détestent et maintenant Lighthouse le fait aussi.

Comment nous avons optimisé React Storefront pour Lighthouse v6.0

Lorsque nous avons testé pour la première fois la page produit de l’application de démarrage React Storefront sur Lighthouse v6.0, en utilisant PageSpeed Insights, elle a obtenu un score dans les 60 plus bas:

Pour améliorer le score, nous avons d’abord mis notre regard sur LCP. À 2,5 secondes, le FCP était extrêmement élevé (nous y reviendrons plus tard), mais l’écart de près de 3 secondes entre le FCP et le LCP s’est distingué comme quelque chose qui nécessitait une amélioration.

Optimisation du LCP

L’image principale du produit de React Storefront (simplement un espace réservé – la boîte verte avec « Product 1 » au centre) a été rendue comme une balise HTML img avec une URL src pointant vers https://via.placeholder.com. Ce site a un TTFB décent (environ 150ms), qui pourrait être amélioré en passant à un CDN plus rapide comme Layer0 CDN-as-JavaScript. Pourtant, nous doutons que 150 ms expliquent l’écart de près de 3 secondes entre FCP et LCP. Pour éliminer l’écart, nous avons inlinisé l’image en utilisant une URL de données base64 comme celle-ci:

				
					<img decoding="async" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAlgAAA…"/>

				
			

Nous l’avons fait en téléchargeant l’image principale, en la convertissant en base64, et en la chargeant dans l’attribut src pendant le rendu côté serveur sur le nuage JavaScript sans serveur de Layer0. Voici la ligne dans le point de terminaison API du produit:

				
					const mainProductImage = result.product.media.full[0]
mainProductImage.src = await getBase64ForImage(mainProductImage.src)
				
			

Et voici le code qui récupère et convertit l’image en base 64:

				
					import fetch from 'node-fetch'

export default async function getBase64ForImage(src) {
  const res = await fetch(src)
  const contentType = res.headers.get('content-type')
  const buffer = await res.buffer()
  return `data:${contentType};base64,${buffer.toString('base64')}`
return `data:${contentType};base64,${buffer.toString('base64')}` return `data:${contentType};base64,${buffer.toString('base64')}` return `data:${contentType};base64,${buffer.toString('base64')}`
}

				
			

Assez simple et vieille école. Et l’impact sur le score ?

En faisant passer le LCP de 5.3s à 2.8s, nous avons gagné 21 points dans le score Lighthouse v6.0 de la page ! C’est un peu troublant comment un si petit changement peut affecter considérablement le score de Lighthouse v6.0, mais nous allons le prendre. Il convient de noter que toutes les mesures varient quelque peu entre les séries, mais que la note globale était constante dans les années 80 les plus faibles Pour le contexte, le site de commerce électronique leader le plus performant sur v6.0 obtient une note de 87 mesurée sur PSI et semble tout droit sorti des années 90- jetez un coup d’œil à www.rockauto.com

L’écart entre FCP et LCP montré ci-dessus était à peu près aussi grand que nous l’avons vu sur plusieurs passages. La plupart du temps, l’écart se situait entre 100 ms et 300 ms. Parfois, FCP et LCP étaient les mêmes.

Optimisation du Service d’orientation pas à pas

Ensuite, nous avons essayé d’améliorer le TBT. C’était assez difficile. Comme mentionné précédemment, pour améliorer LCP, vous devez soit réduire la taille de votre pack JavaScript, soit améliorer le temps d’hydratation. Avec la plupart des applications, il n’est tout simplement pas possible de commencer à extraire les dépendances pour rendre le bundle plus petit. Les applications construites avec React Storefront 7 bénéficient déjà de nombreuses optimisations de compilation qui minimisent la taille du bundle fournie par la configuration du webpack de Next.js, ainsi que d’ optimisations babel spécifiques pour Material UI. Alors, où pourrions-nous nous améliorer ? Temps d’hydratation.

Lazy Hydration pour la victoire !

Heureusement, la communauté React Storefront avait déjà commencé à travailler sur la prise en charge de l’hydratation paresseuse avant que Lighthouse v6.0 ne soit abandonnée. Cela nous a certainement incités à accélérer nos efforts.

Au cas où vous ne l’ignoreriez pas, l’hydratation fait référence à la prise de contrôle par React des éléments HTML rendus sur le serveur afin qu’ils deviennent interactifs. Les boutons deviennent cliquables, les carrousels peuvent être glissés, etc Plus une page contient de composants, plus l’hydratation prend de temps. Les composants complexes, tels que le menu principal et le carrousel d’images du produit, prennent encore plus de temps.

L’hydratation paresseuse consiste à retarder l’hydratation de certains composants jusqu’à ce qu’elle soit absolument nécessaire et surtout, après le chargement initial de la page (et après le calcul du TBT). Une hydratation paresseuse peut être risquée. Vous devez vous assurer que les éléments de la page sont prêts à répondre à l’entrée de l’utilisateur avant que celui-ci tente d’interagir avec eux.

L’implémentation de l’hydratation paresseuse sur React Storefront s’est avérée assez difficile en raison de la dépendance de Material UI sur CSS-in-JS, qui ajoute dynamiquement des styles au document uniquement après l’hydratation des composants. Je vais enregistrer les détails pour une autre fois. En fin de compte, nous avons construit un composant LazyHydrate que les développeurs peuvent insérer n’importe où dans l’arborescence des composants pour retarder l’hydratation jusqu’à ce qu’un événement spécifique se produise, comme l’élément défilant dans la fenêtre d’affichage ou l’utilisateur touchant l’écran.

Voici un exemple où nous hydratons lazy le MediaCarousel qui affiche les images principales du produit:

				
					 import LazyHydrate from 'react-storefront/LazyHydrate'
<LazyHydrate id="carousel" on="touch">
  <MediaCarousel
    ...
  />
</LazyHydrate> 

				
			

Nous avons appliqué l’hydratation paresseuse à plusieurs domaines de l’application, notamment:

  • Le menu coulissant : nous hydratons cela lorsque l’utilisateur appuie sur le bouton hamburger.
  • Toutes les commandes sous le pli : comprennent les sélecteurs de taille, de couleur et de quantité, ainsi que les onglets d’informations sur le produit.
  • Le carrousel d’images principal : ce menu et le menu principal sont probablement les composants les plus fonctionnels et, par conséquent, les plus coûteux à hydrater.

Voici le score Lighthouse v6.0 avec hydratation paresseuse appliquée:

L’hydratation paresseuse a réduit le TBT de près de 40% et réduit le TTI (qui a un poids de 15% sur les scores en v6.0) de 700ms. Cela a permis un gain de 6 points dans le score global Lighthouse v6.0.

Vous remarquerez que le FCP a augmenté un peu, mais le LCP a baissé. Ces petits changements sont essentiellement « dans le bruit » que vous obtenez lorsque vous exécutez PageSpeed Insights. Tous les scores fluctuent légèrement entre les courses.

Optimisation du FCP

Sur la base du score ci-dessus, nous avons estimé que le PCF et/ou le PCF pourraient être encore améliorés. Nous savons que les scripts peuvent bloquer le rendu, nous avons donc examiné comment Next.js importe les scripts dans le document :

				
					<scriptsrc="/_next/static/runtime/main-cb5fd281935517c43f30.js"async=""></script>
				
			

Utiliser async ici pourrait ne pas être le meilleur choix. Si le script est téléchargé pendant le rendu, il peut interrompre le rendu pendant l’évaluation du script, ce qui augmente les temps FCP et LCP. L’utilisation de defer au lieu d’async garantirait que les scripts ne sont évalués qu’après la peinture du document.

Malheureusement, Next.js ne vous permet pas de modifier la façon dont les scripts sont importés, nous avons donc dû étendre le composant NextScript comme suit:

				
					import React from 'react'
import { NextScript as OriginalNextScript } from 'next/document'
/**
 * A replacement for NextScript from `next/document` that gives you greater control over how script elements are rendered.
 * This should be used in the body of `pages/_document.js` in place of `NextScript`.
 */
export default class NextScript extends OriginalNextScript {
  static propTypes = {
    /**
     * Set to `defer` to use `defer` instead of `async` when rendering script elements.
     */
    mode: PropTypes.oneOf(['async', 'defer']),
  }
  static defaultProps = {
    mode: 'async',
  }
  getScripts() {
    return super.getScripts().map(script => {
      return React.cloneElement(script, {
        key: script.props.src,
        defer: this.props.mode === 'defer' ? true : undefined,
        async: this.props.mode === 'async' ? true : undefined,
      })
    })
  }
}

				
			

Ensuite, nous avons ajouté ce qui suit à pages/_document.js:

				
					 <NextScript mode="defer" />
				
			

À notre grand plaisir, cela a amélioré le LCP et les scores globaux :

Il a également légèrement heurté le FCP sur de nombreux passages, mais cela peut être dans le «bruit». Néanmoins, le score global était constamment supérieur de 2-3 points lors de l’utilisation de defer vs async.

Résumé

Lorsque Lighthouse v6.0 a été publié fin mai 2020, les scores de performance de nombreux sites, y compris les applications React Storefront, ont chuté. Avant les optimisations, les performances PDP de l’application de démarrage React Storefront étaient embourbées dans les faibles 60s. Avec ces optimisations, nous l’avons mis dans l’air maintenant raréfié des basses années 90 À ce stade, nous pensons que la seule façon d’améliorer encore le score serait de supprimer les dépendances, ce qui peut signifier d’échanger la productivité des développeurs contre les performances des applications.

C’est une discussion pour une autre fois. Laissez-moi vous laisser avec certaines choses que nous avons essayées qui n’ont pas fonctionné:

Preact

Preact réduit la taille du paquet de 20 à 30 %, mais les scores Lighthouse v6.0 étaient systématiquement pires pour toutes les métriques, même TTI. Nous ne savons pas pourquoi, mais nous savons que ce n’est pas nouveau ou exclusif à Lighthouse v6.0. C’était plus lent avec Lighthouse v5.7 aussi. Nous continuons à vérifier périodiquement et espérons qu’un jour cela sera corrigé.

Chunking

Next.js a récemment introduit un découpage plus fin des actifs du navigateur. Lorsque cela a été introduit pour la première fois dans Next.js 9,1, nous avons remarqué que les morceaux supplémentaires, plus petits, ont en fait aggravé TTI. Cela rend probablement l’application plus rapide pour les utilisateurs qui reviennent après la publication d’une nouvelle version, car elle peut mieux exploiter le cache du navigateur, mais Lighthouse ne se soucie pas de tout cela. React Storefront a donc limité le nombre de morceaux de navigateur à un pendant un certain temps:

				
					const webpack = require('webpack')
const withReactStorefront = require('react-storefront/plugins/withReactStorefront')
module.exports = withReactStorefront({
  target: 'serverless',
  webpack: config => {
    config.plugins.push(
      new webpack.optimize.LimitChunkCountPlugin({
        maxChunks: 1,
      })
    )
    return config
  },
})

				
			

Polices Web

La plupart des sites utilisent une police web personnalisée. Par défaut, React Storefront utilise Roboto (bien que cela puisse être modifié ou supprimé). Les polices web personnalisées tuent les performances, simples et simples. Supprimez la police web et vous gagnerez environ 1 seconde de FCP.

Comme pour les analyses, les parties prenantes semblent disposées à sacrifier les performances pour avoir une police spécifique. React Storefront sert la police Web à partir de la même origine que le site pour éliminer le temps de négociation TLS que vous entriez si vous chargez la police à partir d’un CDN tiers, tel que Google Fonts. Plus précisément, nous utilisons le package typeface-roboto de NPM. Lorsqu’il est combiné avec CSS-loader de Webpack , l’utilisation de typeface-roboto entraîne l’importation de la police via un fichier CSS distinct que le navigateur doit télécharger et analyser. Nous avons pensé que l’incorporation de ce CSS dans le document pourrait aider à la performance, mais ce n’est pas le cas

Quand il s’agit de performances, vous devez toujours mesurer. Les optimisations qui devraient fonctionner en théorie peuvent ne pas être en pratique.

Boostez votre score Lighthouse

Nos clients se classent dans le 95e percentile des 500 meilleurs sites de commerce électronique sur Lighthouse v6.0.

Planifiez une conversation consultative !