遅延読み込みのベスト プラクティス

画像や動画の遅延読み込みには、測定可能なメリットがありますが、軽視すべき作業ではありません。誤ると、意図しない結果が生じる可能性があります。そのため、次の点に留意することが重要です。

スクロールせずに見える範囲に注意

JavaScript を使用してページ上のすべてのメディア リソースを遅延読み込みしたくなるかもしれませんが、そのような誘惑には抵抗する必要があります。スクロールせずに見える範囲にあるものは、遅延読み込みをしないようにする必要があります。このようなリソースは重要なアセットとみなされるため、通常どおり読み込む必要があります。

遅延読み込みは、スクリプトの読み込みが完了して実行が開始される DOM がインタラクティブになるまで、リソースの読み込みを遅らせます。スクロールしなければ見えない範囲の画像の場合は問題ありませんが、スクロールせずに見える範囲にある重要なリソースは、標準の <img> 要素で読み込んで、できるだけ早く表示されるようにする必要があります。

もちろん最近では、ウェブサイトがさまざまなサイズで多種多様な画面で閲覧されているため、スクロールせずに見える範囲はそれほど明確ではありません。ノートパソコンではスクロールせずに見える範囲にあるものは、モバイル デバイスではスクロールしなければ見えない範囲にあると考えられます。あらゆる状況でこれに最適に対処するための確実なアドバイスはありません。ページの重要なアセットのインベントリを作成し、それらの画像を通常の方法で読み込む必要があります。

また、遅延読み込みをトリガーするしきい値ほど、折りたたみ線に関する制約は厳格にしないほうがよい場合もあります。場合によっては、スクロールせずに見える範囲の少し下にバッファゾーンを設定して、ユーザーがビューポートにスクロールする前に画像の読み込みを開始することをおすすめします。たとえば、Intersection Observer API を使用すると、新しい IntersectionObserver インスタンスを作成するときに、オプション オブジェクトに rootMargin プロパティを指定できます。これにより、要素にバッファが効果的に与えられ、要素がビューポートに表示される前に遅延読み込み動作がトリガーされます。

let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
  // lazy-loading image code goes here
}, {
  rootMargin: "0px 0px 256px 0px"
});

rootMargin の値が CSS の margin プロパティに指定した値と似ている場合、それはそのように見えるからです。この場合、監視対象の要素(デフォルトではブラウザのビューポートですが、root プロパティを使用して特定の要素に変更できます)の下余白が 256 ピクセル拡大されます。つまりコールバック関数は、画像要素がビューポートの 256 ピクセル以内にあるときに実行され、ユーザーが実際に画像を見る前に画像の読み込みが開始されます。

Intersection Observe をサポートしていないブラウザでこれと同じ効果を得るには、スクロール イベント処理コードを使用し、getBoundingClientRect チェックを調整してバッファを含めます。

レイアウト シフトとプレースホルダ

メディアの遅延読み込みにより、プレースホルダが使用されない場合、レイアウトがシフトする可能性があります。 この変更により、ユーザーにとって方向性を失い、システム リソースを消費してジャンクの原因となる、負荷の高い DOM レイアウト操作がトリガーされる可能性があります。少なくとも、ターゲット画像と同じ寸法を占める単色のプレースホルダを使用するか、読み込み前にメディア アイテムのコンテンツを示す LQIPSQIP などの手法を使用することを検討してください。

<img> タグの場合、src は、その属性が画像の最終ページ URL で更新されるまで、最初はプレースホルダを指します。<video> 要素の poster 属性を使用して、プレースホルダ画像を指定します。また、<img> タグと <video> タグの両方で width 属性と height 属性を使用します。これにより、プレースホルダから最終画像に遷移しても、メディアの読み込み時に要素のレンダリング サイズが変わらないようになります。

画像のデコードの遅延

大きな画像を JavaScript で読み込んで DOM にドロップすると、メインスレッドがフリーズし、デコード中にユーザー インターフェースが短時間応答しなくなる可能性があります。画像を DOM に挿入する前に、decode メソッドを使って非同期にデコードすると、この種のジャンクを減らすことができます。ただし、注意: 一部の地域はまだ利用できないため、遅延読み込みのロジックが複雑になります。使用する場合は、確認する必要があります。フォールバックで Image.decode() を使用する方法は次のとおりです。

var newImage = new Image();
newImage.src = "my-awesome-image.jpg";

if ("decode" in newImage) {
  // Fancy decoding logic
  newImage.decode().then(function() {
    imageContainer.appendChild(newImage);
  });
} else {
  // Regular image load
  imageContainer.appendChild(newImage);
}

こちらの CodePen リンクで、この例に似たコードが実際に動作する様子をご覧ください。ほとんどの画像がかなり小さい場合は、あまり効果がないかもしれませんが、大きな画像を遅延読み込みして DOM に挿入する際のジャンクは確実に削減できます。

読み込まれない場合

なんらかの理由でメディア リソースの読み込みが失敗し、エラーが発生する場合があります。これはどのような場合に発生する可能性がありますか。場合により異なりますが、1 つの仮定のシナリオがあります。短期間(5 分など)の HTML キャッシュ ポリシーで、ユーザーがサイトにアクセスした、またはユーザーが長期間(数時間など)古いタブを開いたままにして、戻ってコンテンツを読み取った場合です。このプロセスのある時点で、再デプロイが行われます。このデプロイ中に、ハッシュベースのバージョニングによってイメージ リソースの名前が変更されるか、完全に削除されます。ユーザーが画像を遅延読み込みするころにはリソースが利用できなくなり、失敗します。

このようなケースは比較的まれですが、遅延読み込みが失敗した場合は、バックアップ プランを作成しておくことをおすすめします。イメージの場合、このようなソリューションは次のようになります。

var newImage = new Image();
newImage.src = "my-awesome-image.jpg";

newImage.onerror = function(){
  // Decide what to do on error
};
newImage.onload = function(){
  // Load the image
};

エラーの発生時に何をするかは、アプリケーションによって異なります。たとえば、画像のプレースホルダ領域を、ユーザーが画像を再度読み込めるボタンに置き換えることができます。または、画像のプレースホルダ領域にエラー メッセージを表示するだけで済みます。

他のシナリオも考えられます。何をするにせよ、エラーが発生したときにユーザーに通知し、問題が発生した場合にユーザーに取るべきアクションを提示することも重要です。

JavaScript の対応状況

JavaScript が常に利用できるとは限りません。画像を遅延読み込みする場合は、JavaScript が使用できない場合に画像を表示する <noscript> マークアップを提供することを検討してください。最も簡単なフォールバックの例は、JavaScript がオフになっている場合に <noscript> 要素を使用して画像を提供することです。

私は画像だ!

JavaScript がオフになっていると、プレースホルダ画像と <noscript> 要素に含まれる画像の両方がユーザーに表示されます。これを回避するには、次のように <html> タグに no-js のクラスを配置します。

<html class="no-js">

次に、<link> タグを使用してスタイルシートがリクエストされる前に、<head> に 1 行のインライン スクリプトを配置します。これにより、JavaScript がオンの場合に <html> 要素から no-js クラスが削除されます。

<script>document.documentElement.classList.remove("no-js");</script>

最後に、JavaScript が利用できない場合は、いくつかの CSS を使用して遅延クラスの要素を非表示にします。

.no-js .lazy {
  display: none;
}

プレースホルダの画像の読み込みが妨げられることはありませんが、望ましい結果が得られます。JavaScript をオフにしているユーザーは、プレースホルダ画像以上のものが表示されます。これは、プレースホルダや意味のある画像コンテンツをまったく使用しないより適切なものです。