Chromeネイティブレイジーロード

レイジーロードとは?

機会に恵まれ、2019年のGoogle I/Oに参加することができました。ウェブ技術セッションの中で大きく目立ったものはありませんでしたが、一部のセッションで紹介されていたウェブと関連技術に興味深いものがありました。Native Lazy Loading、Portals、Duplex、Rich Resultsなどがそうです。このうち、キーノートや他のセッションでも度々言及されていたネイティブレイジーロード(Native Lazy Loading、レイジーロードのネイティブ実装)が最も期待できました。

レイジーロード、特に画像レイジーロードは、新しい技術ではありません。従来のブラウザはウェブページをローディングする際、初めに全体領域にあたる画像を同時にダウンロードします。ユーザーがウェブページにあるすべての画像を見ないまま、他のウェブページに移動する可能性がありますが、ブラウザは常にすべての画像をダウンロードしています。画像レイジーロードは、最初のページの読み込みで、すべての画像をダウンロードしません。初期にはブラウザのビューポートに表示される領域の画像のみをダウンロードして、残りの画像は必要な状況や可能性が高い状況をヒューリスティクス(heuristics)により判別してダウンロードしています。つまり、ユーザーのスクロールやその他の環境要因を判断して、画像を選択的にダウンロードします。

レイジーロードを適用すると、不要なネットワークコストを削減できるだけでなく、性能、メモリ、使用性のすべての面で有利になります。従来はレイジーロードをJavaScriptで直接実装したり、ライブラリを使用したりしていました。ブラウザにてネイティブサポートに対応した際のレイジーロードは、画像をダウンロードする際の判断をブラウザ内で決定するため、JavaScriptよりも多くの情報にアクセスができ、さらに洗練されたヒューリスティクスによる運用を期待できます。一方、このような判断に介入できるオプションは提供されないようです。Chromeはバージョン75以降、iframeimgのレイジーロードをネイティブでサポートする予定です。

新しい属性 loading

公式レイジーロード設計ドキュメントによると、レイジーロードは、<img><iframe>エレメントに追加された新しい属性loadingで設定できるようです。

<img src="cat.jpg" loading="lazy" />
<iframe src="some.html" loading="lazy"></iframe>

loading属性としてlazy値を付与すると、その画像や子フレームはレイジーロードされます。

<img src="cat.jpg" loading="eager" />
<iframe src="some.html" loading="eager"></iframe>

eager値は、意図的にレイジーロードを使用せずに、従来のブラウザの動作と同じように、ページのローディング時に画像をダウンロードします。状況によっては、レイジーロードが必要ないかもしれません。レイジーロードがサービスの使用性を妨げる場合は、eagerオプションを設定しましょう。

<img src="cat.jpg" loading="auto" />
<img src="cat.jpg" />

autoは、loading属性のデフォルト値として、別途の属性を指定しない場合に適用される値です。ブラウザにレイジーロードするかどうか決定を委ねます。

Chromeは世界で最も多く使用されているブラウザで、日本でも約40%の使用率で最も多く使用されています。(2019年4月時点)

この40%のユーザーを考慮して、あらかじめテストが必要です。特に画像に敏感なサービスであれば、フルバージョンに実装される前に、必ず使い勝手をテストして、サービスに合った適切な対応が必要です。通常は問題ないと予想されますが、デフォルトがauto値なので、使用性で意図しない問題が生じる可能性があります。レイジーロードの導入がまだ検討できないサービスであれば、少なくとも画像ごとにloadingの値をeagerに適用して、既存の動作と同じように維持されるか確認しましょう。もちろん、autoが全く問題ないこともあります。

機能検出とフォールバック

LazyLoad Explainerの文書を見ると、レイジーロードに対応するブラウザの検出方法が分かります。他の機能の検出と似ていますが、画像要素のインターフェースでHTMLImageElementのプロトタイプに、loadingプロパティが存在するかで判断します。

if ('loading' in HTMLImageElement.prototype) alert("ネイティブレイジーロードに対応する"); else alert("対応しない");

loading 属性を適用して簡単に使用することができるため、サービスに悪影響を与える可能性も低いようです。これまでレイジーロードを使用していなかったサービスでも、Chromeではレイジーロードを使用し、他のブラウザでは無視するといったように実装すれば、十分に効果があるでしょう。すべてのブラウザでレイジーロードを提供したい場合は、フォールバックを採択し、他のライブラリを考慮すればよいのです。(もちろん自分で作成できます。)

GoogleのI/Oの「Speed at Scale」のセッションでは、フォールバック策としてlazysizesというライブラリを用いたサンプルコードを公開しました。イメージのコードは可読性が落ちるので、Addy Osmaniのブログで同じコードをコピーしました。

<!-- Let's load this in-viewport image normally -->
<img src="hero.jpg" alt=".."/>

<!-- Let's lazy-load the rest of these images -->
<img data-src="unicorn.jpg" loading="lazy" alt=".." class="lazyload"/>
<img data-src="cats.jpg" loading="lazy" alt=".." class="lazyload"/>
<img data-src="dogs.jpg" loading="lazy" alt=".." class="lazyload"/>

<script>
  if ('loading' in HTMLImageElement.prototype) {
    const images = document.querySelectorAll("img.lazyload");  
    images.forEach(img => {
      img.src = img.dataset.src;
    });
  } else {
    let script = document.createElement("script");
    script.async = true;
    script.src = "https://cdnjs.cloudflare.com/ajax/libs/lazysizes/4.1.8/lazysizes.min.js";
    document.body.appendChild(script);
  }
</script>

lazysizesのようなライブラリは使用していませんが、画像レイジーロードの実装コードがすべて似通っているので、実装内容を推測できます。当初は、すべての<img>要素にプレースホルダの画像を表示し、data-srcパスの画像を適切な状況でダウンロードすると、画像要素のソースに変更するといった形態で実装されたようです。フォールバックのサンプルにある実装内容をみると、初期はlazysizesを使ったコードで作業し、機能検出にネイティブレイジーロードが使用できれば、lazysizesで必要なdata-srcsrcに切り替えて、ネイティブレイジーロードを使用するように要素を変更しています。

まとめ

Addy Osmaniのブログの下段の内容に従って、Chrome Canaryバージョンでテストしてみました。機能検出もでき、Imageオブジェクトのloading値まで確認できました。しかし、レイジーロードは正常に行われず、ウェブページのローディング初期からすべての画像をダウンロードしてしまいました。まだ開発中の機能なので、フルバージョンや、より改善されたCanaryバージョンでは正常に動作するものだと期待しています。テストで使ったCanaryバージョンは76.0.3799.0です。

ネイティブレイジーロードは、FirefoxでもChromeと同スペックで適用される予定であると、ZDNETの記事で紹介されています。MicrosoftのEdgeもChromium基盤で開発されており、Chromeで開始したスペックが自然と標準になっているようです。もちろん良い機能であり、標準スペックとして考慮されるべき機能ですが、昨年の「Chrome is turning into the new Internet Explorer 6」の記事も注目した方がよいでしょう。

TOAST Meetup 編集部

TOASTの技術ナレッジやお得なイベント情報を発信していきます
pagetop