画像の遅延読み込み

3 min read読了の目安(約3300字

はじめに

この記事に出会ってくれてありとうございます。

スマートアプリでインターンをしているぷっちょです。主に, NFTマーケットプレイス nanakusaの開発を行っています。
今回は画像の遅延読み込みについて書こうと思います。

概要

画像を多く表示するWebサイトでは画面の読込に時間がかかり、パフォーマンスやUXの低下につながります。
nanakusaでも、一度に多くの画像が読み込まれていため、パフォーマンスが悪かったです。

この問題の簡単な解消方法の一つにLazy Loadingがあります。

Lazy Loadingとは

Lazy Loadingは、ページへのリソースの読み込みを、事前に読み込むのではなく実際に必要になった時点まで遅延することです。Lazy Loadingは、パフォーマンスの向上、および関連するコストの削減に役立ちます。


クレジット: Chromium Blog

loading属性

Chrome, Edge, Firefoxでは、<img loading="lazy"> 属性を利用できます。Safariではまだサポートされていません.

Chrome76以降では、このloading属性を使用して、スクロールしてアクセスできる画像の読み込みを完全に遅延することができます.

<img src="image.png" loading="lazy" alt="" width="200" height="200">

loading属性がサポートされてないブラウザでの処理

いくつか方法はあるみたいですが、今回はIntersection Observer APIを利用しました。

まず、要素を次のように定義する必要があります

<img loading="lazy" src="lazy_img.jpg" data-src="real_img.jpg" />

src属性は、実際の画像が読み込まれる前の、初期画像です。data-srcは、要素がブラウザのビューポートに入るときに要素に読み込まれる実際の画像です。

次に,交差点オブザーバーのインスタンスを作成します

const imageObserver = new IntersectionObserver...;

IOは関数を受け取ります。この関数には、2つのパラメータがあります。1つは監視対象の要素で構成される配列を持ち、2つ目のパラメータはIOのインスタンスを持ちます。

const imageObserver = new IntersectionObserver((entries、imgObserver)=> { 
    // ... 
};

entriesは配列なので、その中の要素を取得して各要素に対して遅延読み込みを実行するには、entriesをループする必要があります。

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

entryごとに、ビューポート内にあるかどうか確認します。ビューポートと交差している場合は、img要素のsrc属性をdata-src属性の値に設定します。

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

そして、imageObserverを呼び出して、img要素を監視します。

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

code sample

<img src = "lazy_img.jpg" data-src = "img_1.jpg" />
<img src = "lazy_img.jpg" data-src = "img_2.jpg" />
...

...
<script> 
        document.addEventListener( "DOMContentLoaded"function( ){ 
            const imageObserver = new IntersectionObserver((entries、imgObserver)=> { 
                entries.forEach((entry)=> { 
                    if(entry.isIntersecting){ 
                        const lazyImage = entry.target 
                        lazyImage.src = lazyImage.dataset.src 
                    } 
                }};
            const arr = document.querySelectorAll('img[loading="lazy"]')
            arr.forEach((v)=> { 
                imageObserver.observe(v); 
            }}</script>


結果

この処理により、かなりパフォーマンスが改善しました。
(このlazy loading以外にも、サムネイル画像のサイズを圧縮するなどの処理も入れました。)

https://nanakusa.io/market

最後に

最後まで見ていただきありがとうございます。
この記事が良いなと思ったらいいねお願いしますっ

スマートアプリでは、エンジニアを募集しています!副業もOKです。
また、インターン生のエンジニアも募集しています!!フルリモートです。僕と一緒に働いてくれる仲間を探しています。興味のある方はTwitterからDMお願いします!Twitter

https://smartapp.co.jp/recruit/

Enjoy Hacking! 😎