Chargement d'images paresseuses à l'aide d'IntersectionObserver

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.

image

Dispositions générales


Considérez, par exemple, la page d' accueil du département Home de Walmart.


Une page avec de nombreuses images

Voici des informations sur le nombre d'images chargées pour former cette page:


Images chargées lors de la formation de la page

Comme 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 page

Dans 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'image

Afin de mettre en œuvre la technique de chargement paresseux d'images dans ce composant, vous devez effectuer les trois étapes suivantes:

  1. Ne restituez pas l'image immédiatement après le téléchargement.
  2. Configurez des outils pour détecter l'apparence d'une image dans la zone de visualisation du contenu de la page.
  3. 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 paresseuse

Cependant, 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éseau

Comme 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 paresseuses

En 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'optimisation


Rapport phare généré après optimisation

Ré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?

Source: https://habr.com/ru/post/fr423485/


All Articles