De nos jours, la principale pierre d'achoppement sur le chemin vers des sites de chargement à grande vitesse est l'image. Cela est particulièrement vrai pour les projets de commerce électronique. Les images qui s'y trouvent, généralement assez «lourdes», constituent l'essentiel du contenu des pages. Ceci, en règle générale, conduit au fait que pour afficher une page à l'utilisateur, son navigateur doit télécharger plusieurs mégaoctets de données graphiques. Comment accélérer le chargement des pages dans cette situation? La réponse à cette question est consacrée au matériel dont nous publions aujourd'hui la traduction.

Dispositions générales
Considérez, par exemple, la page d'
accueil du département
Home de Walmart.
Une page avec de nombreuses imagesVoici des informations sur le nombre d'images chargées pour former cette page:
Images chargées lors de la formation de la pageComme vous pouvez le voir, il y a 137 images! Cela signifie que plus de 80% des données nécessaires à l'affichage de la page et transmises sur le réseau sont présentées sous forme de fichiers graphiques.
Nous analysons maintenant les requêtes réseau qui sont exécutées au chargement de la page:
Requêtes réseau exécutées lors de la formation de la pageDans ce cas, les fichiers résultant de la séparation du code projet sont téléchargés plus tard qu'ils ne le pouvaient. Cela est dû au fait que vous devez d'abord charger le bundle principal
cp_ny.bundle
. Cet ensemble pourrait être téléchargé beaucoup plus rapidement s'il n'avait pas été perturbé par 18 images en concurrence les unes avec les autres pour la bande passante.
Comment y remédier? En fait, pour vraiment «réparer» cela ne fonctionnera pas, mais vous pouvez faire beaucoup de choses afin d'optimiser le chargement de l'image. Il existe de nombreuses approches pour optimiser les images utilisées sur les pages Web. Parmi eux, l'utilisation de divers formats de fichiers graphiques, la compression des données, l'utilisation de la technique d'animation floue, l'utilisation de CDN. Je voudrais m'arrêter sur le soi-disant "chargement paresseux" des images (chargement paresseux). En particulier, nous parlerons de la façon de mettre en œuvre cette technique sur les sites React, mais comme elle est basée sur des mécanismes JavaScript, elle peut être intégrée dans n'importe quel projet Web.
Projet pilote
Commençons par un composant
Image
React extrêmement simple:
class Image extends PureComponent { render() { const { src } = this.props; return <img align="center" src={src} />; } }
Il prend comme propriété une URL et l'utilise pour rendre l'élément HTML
img
.
Voici le code JSFiddle pertinent. L'image suivante montre la page contenant ce composant. Veuillez noter que pour voir l'image affichée par lui, vous devez faire défiler le contenu de la page.
La page avec le composant qui affiche l'imageAfin de mettre en œuvre la technique de chargement paresseux d'images dans ce composant, vous devez effectuer les trois étapes suivantes:
- Ne restituez pas l'image immédiatement après le téléchargement.
- Configurez des outils pour détecter l'apparence d'une image dans la zone de visualisation du contenu de la page.
- Affichez l'image après avoir détecté qu'elle est tombée dans la zone de visualisation.
Jetons un coup d'œil à ces étapes.
Étape 1
À cette étape, l'image immédiatement après le chargement n'est pas affichée.
render() { return <img />; }
Étape 2
Ici, nous configurons les mécanismes qui nous permettent de détecter le moment où l'image entre dans la zone de visualisation.
componentDidMount() { this.observer = new IntersectionObserver(() => { // }, { root: document.querySelector(".container") }); this.observer.observe(this.element); } .... render() { return <img ref={el => this.element = el} />; }
Analysons ce code. Voici ce qui a été fait ici:
- L'attribut ref a été ajouté à l'élément
img
. Cela vous permet de mettre à jour ultérieurement le lien d'image dans src
sans avoir à restituer le composant. - Une nouvelle instance d'
IntersectionObserver
(nous en parlerons ci-dessous). - L'objet
IntersectionObserver
est invité à observer l'image à l'aide de la construction observe(this.element)
.
Qu'est-ce que
IntersectionObserver
? Considérant que le mot «intersection» est traduit par «intersection» et «observateur» est «observateur», on peut déjà deviner le rôle de cet objet. Si vous recherchez des informations à ce sujet sur
MDN , vous pouvez découvrir que l'API Intersection Observer permet aux applications Web de surveiller de manière asynchrone les changements dans l'intersection d'un élément avec son parent ou la portée d'un document de fenêtre.
À première vue, cette caractéristique de l'API peut ne pas sembler particulièrement compréhensible, mais, en fait, sa structure est très simple. L'instance
IntersectionObserver
reçoit plusieurs paramètres. En particulier, nous avons utilisé le paramètre
root
, qui nous permet de définir l'élément racine DOM, que nous considérons comme un conteneur, à propos de l'intersection de l'élément avec la frontière dont nous avons besoin de connaître. Par défaut, c'est la zone dans laquelle se trouve le fragment visible de la page (fenêtre), mais je l'ai explicitement défini pour utiliser le conteneur situé dans l'élément
iframe
de JSFiddle. Ceci est fait afin, plus tard, d'envisager une possibilité qui n'est pas conçue pour utiliser des éléments
iframe
.
La raison pour laquelle utiliser
IntersectionObserver
pour déterminer le moment où un élément devient visible est plus populaire que les méthodes plus traditionnelles, telles que l'utilisation
onScroll
et
getBoundingClientRect()
, car les mécanismes
IntersectionObserver
s'exécutent en dehors du thread principal. Cependant, le rappel appelé après que
IntersectionObserver
détecte que l'intersection de l'élément avec le conteneur est exécuté naturellement dans le thread principal, donc son code ne doit pas être trop lourd.
Étape 3
Nous devons maintenant configurer le rappel qui est appelé lorsqu'il détecte l'intersection de l'élément
target
(
this.element
dans notre cas) avec le conteneur
root
(dans notre cas, c'est un élément
div
.container
).
.... this.observer = new IntersectionObserver( entries => { entries.forEach(entry => { const { isIntersecting } = entry; if (isIntersecting) { this.element.src = this.props.src; this.observer = this.observer.disconnect(); } }); }, { root: document.querySelector(".container") } ); ....
Lorsque l'intersection est détectée, le tableau d'
entries
transféré vers le
entries
, qui ressemble à un ensemble d'instantanés de l'état de tous les éléments cibles pour lesquels l'intersection de la bordure spécifiée est détectée. La propriété
isIntersecting
indique la direction de l'intersection. Si l'élément surveillé ne fait pas partie de l'élément racine, c'est
true
. Si un élément quitte l'élément racine, alors c'est
false
.
Donc, quand il s'avère que l'élément a traversé la bordure inférieure du conteneur, je définis manuellement sa propriété
src
et désactive sa surveillance, ce qui n'est plus nécessaire.
Étape 4 (secrète)
Maintenant, à la quatrième étape secrète de notre travail, vous pouvez admirer le résultat et profiter du succès. Voici le
code qui recueille ce dont nous venons de parler.
Le résultat de l'application de la technique de chargement d'image paresseuseCependant, si vous regardez de plus près ce que nous avons, il se trouve qu'ici vous pouvez trouver quelque chose de pas très bon. Afin de voir cela, j'ai rapidement fait défiler la page, tout en ralentissant la vitesse de la connexion réseau.
Comportement de la page lorsqu'elle défile rapidement et ralentit la vitesse de connexion réseauComme nous ne chargeons l'image qu'après avoir atteint la zone dans laquelle elle devrait déjà être visible, l'utilisateur n'a pas la possibilité de faire défiler la page et de voir la zone occupée par l'image, et, bien sûr, l'image elle-même, avant de la charger. Lorsque des sites consultent des ordinateurs ordinaires connectés à Internet rapide, cela ne pose aucun problème. Mais de nombreux utilisateurs modernes visitent des sites depuis leur téléphone, parfois ils utilisent des réseaux 3G ou, pire encore, des connexions EDGE.
Certes, traiter ce problème n'est pas si difficile. Cela peut être dû au fait que l'API Intersection Observer offre au développeur la possibilité d'étendre ou de réduire les limites de l'élément racine (dans notre cas, c'est l'élément
.container
). Afin d'utiliser cette opportunité, ajoutez simplement une ligne de code à l'endroit où le conteneur racine est configuré:
rootMargin: "0px 0px 200px 0px"
Dans la propriété
rootMargin
, écrivez une ligne dont la structure est conforme aux règles CSS utilisées pour configurer l'indentation des éléments. Dans notre cas, nous informons le système que la bordure inférieure utilisée pour détecter l'intersection d'un élément avec un conteneur doit être augmentée de 200 pixels. Cela signifie que le rappel correspondant sera appelé lorsque l'élément tombe dans une zone située à 200 pixels sous la bordure inférieure de l'élément racine (la valeur par défaut est 0).
Voici le code qui implémente cette technique.
Amélioration de la technique de chargement d'images paresseusesEn conséquence, il s'avère que lorsque nous faisons défiler la page uniquement vers le 4ème élément de la liste, l'image est chargée dans une zone qui est à 200 pixels en dessous de la zone visible de la page.
Maintenant, il semblerait, tout ce qui est nécessaire est fait. Mais ce n'est pas le cas.
Problème de hauteur d'image
Si vous avez soigneusement étudié les illustrations GIF ci-dessus, vous remarquerez peut-être que la barre de défilement fait un "saut" après le chargement de l'image. Heureusement, ce problème est facile à gérer. Sa raison en est que l'élément qui affiche l'image a initialement une hauteur de 0 qui, après chargement de l'image, s'avère être de 300 pixels. Par conséquent, pour résoudre le problème, il suffit de définir l'élément à une hauteur fixe en ajoutant l'attribut
height={300}
à l'image.
À propos des résultats d'optimisation
Quels résultats avons-nous obtenus chez Walmart après avoir appliqué le chargement d'images paresseuses sur
cette page? En fait, les résultats spécifiques varient considérablement en fonction de nombreuses circonstances, parmi lesquelles on peut noter la vitesse de connexion réseau du client, la disponibilité du CDN, le nombre d'images sur la page et les règles de détection des intersections avec l'élément racine qui leur est appliqué. En d'autres termes, pour vous, afin d'évaluer l'impact du chargement paresseux d'images sur votre propre projet, il est préférable de l'implémenter et de le vérifier vous-même. Mais si vous êtes toujours intéressé à regarder ce que le chargement d'images paresseux nous a donné, voici quelques rapports Lighthouse. Le premier est formé avant l'optimisation, le second après.
Rapport sur les phares généré avant l'optimisationRapport phare généré après optimisationRésumé
Aujourd'hui, nous avons examiné une technique pour optimiser les pages Web à l'aide du chargement d'images paresseuses. Si les pages de votre site sont remplies d'images, alors, très probablement, cette technique vous sera utile.
Chers lecteurs! Comment optimisez-vous les images et leur chargement?