API IntersectionObserver et chargement d'images paresseuses

L'utilisation du chargement d'images paresseuses pour améliorer les performances des projets Web est une technique d'optimisation populaire. Le fait est que les images sont des ressources «lourdes» qui sont bondées de sites Web modernes. Nous avons déjà publié quelque chose à ce sujet. Ici, vous pouvez lire ce que le chargement paresseux a donné au site Web Walmart et apprendre à utiliser IntersectionObserver dans les projets React. Voici un article sur l'optimisation des sites statiques. Voici des informations récentes sur l'implémentation du chargement paresseux à l'aide des outils du navigateur.



Aujourd'hui, nous attirons votre attention sur la traduction d'un article dans lequel l'utilisation de l'API IntersectionObserver est décrite en utilisant une simple page Web comme exemple. Ce matériel est destiné aux programmeurs débutants.

Qu'est-ce que le chargement d'images paresseuses?


Lors de l'analyse des performances des applications, deux indicateurs viennent au premier plan: le temps de la première interactivité (Time To Interactive) et la consommation des ressources (Resources Consumption). Ils devront inévitablement faire face à ceux qui sont engagés dans le développement web. De plus, des problèmes avec les applications peuvent survenir non seulement parce qu'ils mettent beaucoup de temps à se préparer au travail ou à consommer trop de ressources. Mais, dans tous les cas, il est très important de trouver les sources de ces problèmes le plus tôt possible et de veiller à ce que les problèmes ne se posent même pas.

L'utilisation de technologies de chargement d'images paresseuses dans des applications Web minimise le risque de problèmes de performances. À savoir, si nous analysons le chargement paresseux du point de vue de son influence sur le moment de mettre la page en état de fonctionnement et la consommation de ressources, nous obtenons ce qui suit:

  1. Temps de première interactivité. Il s'agit du temps dont l'application Web a besoin pour charger et mettre l'interface dans un état adapté à l'utilisateur pour travailler avec elle. Le chargement paresseux (de plus, nous ne parlons pas seulement des images) optimise le temps de réponse des applications grâce à la technologie de séparation du code et de chargement uniquement de ce qui est nécessaire pour une page particulière, ou de ce qui est nécessaire à un moment précis.
  2. Consommation de ressources. Les humains sont des êtres impatients. Si un site Web a besoin de plus de 3 secondes pour se charger, 70% des utilisateurs quittent ce site. Les applications Web ne devraient pas se charger aussi longtemps. Le chargement paresseux vous permet de réduire la quantité de ressources nécessaires pour faire fonctionner les pages. Par exemple, il se peut que le code d'un certain projet soit divisé en fragments qui ne sont chargés que sur les pages qui en ont besoin. Par conséquent, les performances du site augmentent et la consommation des ressources système diminue.

Ainsi, les technologies de chargement paresseux accélèrent les applications, réduisant le temps qu'elles téléchargent et font apparaître. Ceci est réalisé du fait que les ressources ne sont chargées que lorsqu'elles sont nécessaires.

Voici quelques avantages que le chargement paresseux offre aux projets Web:

  • Les pages se chargent plus rapidement et deviennent opérationnelles.
  • Lors du chargement initial des pages, vous devez transférer et traiter moins de données.

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


Si vous pensez à la façon dont les pages Web se chargent, il devient clair que cela n'a aucun sens de télécharger des images ou d'autres ressources qui ne sont pas visibles par l'utilisateur. Lorsque vous utilisez le chargement différé, les images qui se trouvent dans la zone visible de la page sont d'abord chargées. Ensuite, lorsque l'utilisateur fait défiler la page, d'autres images sont chargées qui tombent dans la zone visible de la page.

Prenons un exemple.


Page et sa zone visible

Jetez un oeil à la figure précédente. Ici, vous pouvez voir le navigateur et la page Web chargés dans celui-ci. Les images #IMG_1 et #IMG_2 font partie de la portée de la page. Cela signifie qu'ils sont visibles pour l'utilisateur et se trouvent dans les limites de la zone de la fenêtre du navigateur qui s'affiche à l'écran.

Cet ordre de travail avec la page ne peut être considéré comme idéal lorsque, lors de son chargement, les images #IMG_1 , #IMG_2 , #IMG_3 et #IMG_4 . Seuls #IMG_1 et #IMG_2 sont visibles pour l' #IMG_2 , et #IMG_3 et #IMG_4 sont cachés. Si vous chargez les première et deuxième images lors du chargement de la page, mais ne téléchargez pas les troisième et quatrième images, cela pourrait avoir un impact positif sur les performances du site. À savoir, nous parlons de ce qui suit. Lorsque l'utilisateur fait défiler la page pour que la troisième image devienne visible, elle se charge. Si le défilement continue et que la quatrième image devient visible, elle se charge également.


Faire défiler une page et charger des images

Maintenant que nous avons compris exactement comment nous voulons travailler avec les images, nous nous demandons comment découvrir qu'un certain élément d'une page tombe dans sa zone visible. Les navigateurs modernes ont une API qui permet au programmeur de savoir quand une certaine zone de la page devient visible. Il s'agit de l'API IntersectionObserver. Il permet, à l'aide de mécanismes asynchrones, d'organiser la surveillance des éléments et de recevoir des notifications dans les cas où un élément franchit la portée du document ou franchit la frontière d'un autre élément.

Afin de configurer le chargement paresseux des images, nous devons d'abord préparer un code de modèle pour l'élément qui sera utilisé pour décrire les images. Le voici:

 <img class="lzy_img" src="lazy_img.jpg" data-src="real_img.jpg" /> 

La classe vous permet d'identifier un élément comme une image à laquelle le chargement paresseux sera appliqué. L'attribut src vous permet d'afficher une image d'espace réservé avant d'afficher l'image réelle. data-src stocke l'adresse de l'image réelle, qui sera chargée lorsque l'élément entrera dans la zone visible de la page.

Écrivons maintenant le code qui implémente le chargement différé. Comme déjà mentionné, nous utiliserons l'API IntersectionObserver pour détecter le moment où l'image entre dans la zone de visualisation de la page.

Créez d'abord une instance de IntersectionObserver :

 const imageObserver = new IntersectionObserver(...); 

Le constructeur IntersectionObserver accepte une fonction avec deux paramètres. L'un d'eux stocke un tableau composé d'éléments qui doivent être surveillés, l'autre une instance IntersectionObserver . Cela ressemble à ceci:

 const imageObserver = new IntersectionObserver((entries, imgObserver) => {   entries.forEach((entry) => {       //...   }) }); 

Le code de fonction vérifie si les images représentées par les entries tableau d' entries croisent la fenêtre. Si tel est le cas, l'attribut src de l'image correspondante enregistre ce qui était dans son attribut data-src .

 const imageObserver = new IntersectionObserver((entries, imgObserver) => {   entries.forEach((entry) => {       if(entry.isIntersecting) {           const lazyImage = entry.target           lazyImage.src = lazyImage.dataset.src       }   }) }); 

Ici, nous vérifions, en utilisant la condition if(entry.isIntersecting) {...} , si l'élément traverse la zone de visualisation du navigateur. Si c'est le cas, nous enregistrons l'élément img dans la constante lazyImage . Ensuite, nous écrivons dans son attribut src ce qui était dans son attribut data-src . Grâce à cela, l'image dont l'adresse est stockée dans data-src est chargée et affichée. Dans le navigateur, cela ressemble à remplacer l'image d'espace réservé, lazy_img.jpg , par une image réelle.

Nous devons maintenant utiliser la méthode .observe() de notre instance IntersectionObserver afin de commencer à surveiller les éléments qui nous intéressent:

 imageObserver.observe(document.querySelectorAll('img.lzy_img')); 

Ici, nous sélectionnons tous les éléments img avec la classe lzy_img le document.querySelectorAll('img.lzy_img') avec la commande document.querySelectorAll('img.lzy_img') et les passons à la méthode .observe() . Ayant reçu une liste d'éléments, il entame le processus d'observation.

Afin de tester cet exemple, nous commençons par initialiser le projet Node.js:

 mkdir lzy_img cd lzy_img npm init -y 

Créez maintenant le fichier lzy_img dans le dossier lzy_img :

 touch index.html 

Ajoutez-y le code suivant:

 <html> <title>Lazy Load Images</title> <body>   <div>       <div style="">           <img class="lzy_img" src="lazy_img.jpg" data-src="img_1.jpg" />           <hr />       </div>       <div style="">           <img class="lzy_img" src="lazy_img.jpg" data-src="img_2.jpg" />           <hr />       </div>       <div style="">           <img class="lzy_img" src="lazy_img.jpg" data-src="img_3.jpg" />           <hr />       </div>       <div style="">           <img class="lzy_img" src="lazy_img.jpg" data-src="img_4.jpg" />           <hr />       </div>       <div style="">           <img class="lzy_img" src="lazy_img.jpg" data-src="img_5.jpg" />           <hr />       </div>   </div>   <script>       document.addEventListener("DOMContentLoaded", function() {           const imageObserver = new IntersectionObserver((entries, imgObserver) => {               entries.forEach((entry) => {                   if (entry.isIntersecting) {                       const lazyImage = entry.target                       console.log("lazy loading ", lazyImage)                       lazyImage.src = lazyImage.dataset.src                   }               })           });           const arr = document.querySelectorAll('img.lzy_img')           arr.forEach((v) => {               imageObserver.observe(v);           })       })   </script> </body> </html> 

Vous pouvez remarquer que 5 images sont décrites ici, dans lesquelles l' lazy_img.jpg réservé lazy_img.jpg s'affiche lors du chargement. L'attribut data-src de chacun d'eux contient des informations sur des images réelles. Voici une liste des noms d'images utilisés dans le projet:

 lazy_img.jpg img_1.jpg img_2.jpg img_3.jpg img_4.jpg img_5.jpg 

Vous devez créer toutes ces images vous-même en amenant le dossier du projet dans la vue illustrée dans la figure suivante.


Dossier de projet

Dans mon cas, le fichier lazy_img.jpg préparé à l'aide de Windows Paint et les images réelles ( img_*.jpg Jpg ) ont été prises à partir de pixabay.com .

Notez que le rappel utilisé pour créer l'instance IntersectionObserver a un appel console.log() . Cela nous permettra d'apprendre à effectuer des opérations pour charger différentes images.

Maintenant, pour servir index.html , nous utilisons le package http-server :

 npm i http-server 

Ajoutez la propriété start à la section scripts du fichier package.json :

 "scripts": {   "start": "http-server ./" } 

Après cela, exécutez la commande npm run start dans le terminal.

Ouvrez maintenant le navigateur et accédez à l'adresse 127.0.0.1:8080 . Notre page index.html au tout début ressemblera à la figure suivante.


Page sans images réelles

Vous remarquerez peut-être que là où les images réelles devraient se trouver, seuls les espaces réservés sont maintenant affichés. Ici, nous partons de l'hypothèse que la première image est dans la zone de visualisation, donc le navigateur commence à la télécharger. Par conséquent, la page ressemblera maintenant à ceci.


Le navigateur a téléchargé la première image

Autres images non encore téléchargées. Ils n'ont pas encore atteint la zone d'observation.

Si vous faites défiler la page tout en regardant la console, vous remarquerez un problème. Il consiste en ce que lors du défilement de la même image dans la fenêtre de visualisation, le navigateur essaie de la télécharger plusieurs fois.


Téléchargements multiples de la même image

Ce n'est pas du tout ce dont nous avons besoin. Cela entraîne un gaspillage inutile des ressources système et dégrade les performances du projet.

Afin de résoudre ce problème, nous devons supprimer l'élément img , dont l'image réelle est déjà chargée, de la liste des éléments que notre instance IntersectionObserver surveille. Nous devons également supprimer la classe lzy_img de cet élément. Voici à quoi cela ressemble dans le code de fonction de rappel modifié:

 <script>   document.addEventListener("DOMContentLoaded", function() {       const imageObserver = new IntersectionObserver((entries, imgObserver) => {           entries.forEach((entry) => {               if (entry.isIntersecting) {                   const lazyImage = entry.target                   console.log("lazy loading ", lazyImage)                   lazyImage.src = lazyImage.dataset.src                   lazyImage.classList.remove("lzy_img");                   imgObserver.unobserve(lazyImage);               }           })       });       const arr = document.querySelectorAll('img.lzy_img')       arr.forEach((v) => {           imageObserver.observe(v);       })   }) </script> 

Après avoir chargé l'image réelle de l'élément img correspondant, sa classe lzy_img est lzy_img et l'élément est exclu de la liste des éléments observés par IntersectionObserver .

Voici l'exemple de code:

 <html> <title>Lazy Load Images</title> <body>   <div>       <div style="">           <img class="lzy_img" src="lazy_img.jpg" data-src="img_1.jpg" />           <hr />       </div>       <div style="">           <img class="lzy_img" src="lazy_img.jpg" data-src="img_2.jpg" />           <hr />       </div>       <div style="">           <img class="lzy_img" src="lazy_img.jpg" data-src="img_3.jpg" />           <hr />       </div>       <div style="">           <img class="lzy_img" src="lazy_img.jpg" data-src="img_4.jpg" />           <hr />       </div>       <div style="">           <img class="lzy_img" src="lazy_img.jpg" data-src="img_5.jpg" />           <hr />       </div>   </div>   <script>       document.addEventListener("DOMContentLoaded", function() {           const imageObserver = new IntersectionObserver((entries, imgObserver) => {               entries.forEach((entry) => {                   if (entry.isIntersecting) {                       const lazyImage = entry.target                       console.log("lazy loading ", lazyImage)                       lazyImage.src = lazyImage.dataset.src                       lazyImage.classList.remove("lzy_img");                       imgObserver.unobserve(lazyImage);                   }               })           });           const arr = document.querySelectorAll('img.lzy_img')           arr.forEach((v) => {               imageObserver.observe(v);           })       })   </script> </body> </html> 

Résumé


Nous avons examiné l'exemple le plus simple de mise en œuvre du système de chargement paresseux d'images. Mais IntersectionObserver vous permet de créer des schémas de chargement assez complexes et paresseux. Si vous êtes intéressé par ce sujet, nous vous recommandons de consulter les publications mentionnées au début du matériel et de lire la documentation .

Chers lecteurs! Avez-vous des exemples d'amélioration des performances du site Web après avoir implémenté un système de chargement d'images paresseux?

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


All Articles