PerfData

大量の画像の読込

decoding="async"とloading="lazy"の違い

ページ作成日 2019年12月19日
ページ更新日 2021年3月2日
著者: 竹洞 陽一郎

Webページにおいて、画像は重要な役割を果たしており、高解像度化と利用される枚数は増えていく傾向にあります。
その一方で、画像は、従来どおりの実装をしていると、HTMLのパース処理を遅延させる要因となります。
画像をいかに遅延要因としないようにするかの処理として、decoding="async"とloading="lazy"の違いを解説します。

decoding="async"

decoding属性は、画像のデコード処理に関しての指示をユーザエージェントに明示する属性です。
decoding="async"と指定することで、画像のデコード処理を非同期にバッググラウンド処理し、他のコンテンツの表示処理が終わった時に更新処理で画像を一緒に出します。

記述の仕方としては、以下のようになります。


<img class="hoge" decoding="async" src="/images/hage.jpg" alt="hage">
decoding属性の値と意味
意味
sync他のコンテンツと連動してデコード処理を行う
async他のコンテンツと非連動してデコード処理を行い、他のコンテンツの表示が遅延しないようにする
autoデコード処理について、明示的に指定しない(デフォルト値)

ブラウザの対応状況

HTML Living Standardの仕様として標準化されていて、IE以外のメジャーなブラウザで対応しています。

Can I Use...でのdecoding属性のブラウザ対応表

HTML Living Standardの仕様、4.8.4.3.4 Decoding imagesについて確認しましょう。

画像のデコード処理

まず、画像のデコード処理について解説が書かれています。

Image data is usually encoded in order to reduce file size.
This means that in order for the user agent to present the image to the screen, the data needs to be decoded.
Decoding is the process which converts an image's media data into a bitmap form, suitable for presentation to the screen.
Note that this process can be slow relative to other processes involved in presenting content.
Thus, the user agent can choose when to perform decoding, in order to create the best user experience.

画像データは通常、ファイルサイズを小さくするために符号化されます。
ユーザエージェントが画面に画像を表示するためには、データを復号化する必要があることを意味します。
デコードとは、画像のメディアデータを、画面表示に適したビットマップ形式に変換するプロセスです。
このプロセスは、コンテンツの表示に関連する他のプロセスに比べて時間がかかる場合があります。
したがって、ユーザエージェントは、最良のユーザエクスペリエンスを生成するために、いつ復号化を実行するかを選択しても良いわけです。

デコードとは、画像のメディアデータを、画面表示に適したビットマップ形式に変換するプロセスであると用語の定義をしています。

画像の同期処理

Image decoding is said to be synchronous if it prevents presentation of other content until it is finished.
Typically, this has an effect of atomically presenting the image and any other content at the same time.
However, this presentation is delayed by the amount of time it takes to perform the decode.

画像復号化は、それが終了するまで他のコンテンツの提示を妨げる場合には、同期であると言われます。
一般に、画像と任意の他のコンテンツを同時にアトミック的(※)に表示する効果を持ちます。
ただし、この表示は、デコードの実行にかかる時間だけ遅延します。

(※訳注 アトミック的とは、atomicityというコンピュータ用語から来ている。一連の処理が不可分の一体として成功・失敗いずれかになり、かつ処理途中の状態に他からアクセスできないこと。)

画像の非同期処理

Image decoding is said to be asynchronous if it does not prevent presentation of other content.
This has an effect of presenting non-image content faster.
However, the image content is missing on screen until the decode finishes.
Once the decode is finished, the screen is updated with the image.

画像復号化は、それが他のコンテンツの表示を妨げない場合、非同期であると言われます。
これには、非イメージコンテンツをより速く表示する効果があります。
ただし、デコードが終了するまで、画面には画像の内容が表示されません。
デコードが完了すると、画面はイメージと共に更新されます。

同期と非同期の違い

In both synchronous and asynchronous decoding modes, the final content is presented to screen after the same amount of time has elapsed.
The main difference is whether the user agent presents non-image content ahead of presenting the final content.

同期と非同期、どちらのデコードモードでも、同じ時間が経過した後に最終的な内容が画面に表示されます。
主な違いは、ユーザエージェントが最終コンテンツを提示する前に非画像コンテンツを提示するかどうかである。

仕様上はこのように書かれていますが、ここに実際の効果において大きな違いがあります。
Webの表示速度を高速化するためには、DOMツリーを如何に高速に構築し、CSSオブジェクトモデルを適用するかが鍵を握ります。
画像のデコーディングを非同期にすることで、HTMLのパース処理で画像のデコーディング時間が入らなくなり、その分、時間が短縮されるのです。

実際、この仕様を提案したAppleのSimon Fraser氏は、"decode" attribute on <img> #1920で、以下のように述べています。

Decoding of large images can block the main thread for hundreds of milliseconds or more, interrupting fluid animations and user interaction. Currently, there's no way for a web author to specify that they want an image to be decoded asynchronously, so there are scenarios where it is impossible to avoid UI stalls.

To solve this problem we propose an "async" attribute on image elements. This attribute is a hint to the UA that the author has requested asynchronous decoding. This implies that if the UA paints an image after the "load" event has fired, but before the image has been decoded, the UA is allowed to not paint the image (rather than block on decoding it).

To notify authors when a decoded image frame is available, we propose firing a new event, "ready", on the image element. This would allow authors who require a fully-decoded image, in content that is sensitive to UI stalls, to wait for the "ready" event before doing something that brings the image into view (such as a CSS transition).

Some images repeatedly decode frames, for example, animated GIFs. In addition, the UA can throw away the decoded frame for a still image, requiring a re-decode. In these cases, we propose that the "ready" event only fires once, the first time a frame is available for display.

ISSUES:
"async" is currently used to imply async loading, and it's possible that we'd want to use it in this sense for images too. Maybe call the new attribute "asyncDecode" or something else.

This only solves the problem for image elements, not CSS images. We could add a CSS property to allow async decoding for CSS images, and fire events on the element to which it applies.

以下に、日本語に訳したものを掲載します。

大きなイメージをデコードすると、メインスレッドが数百ミリ秒以上ブロックされ、滑らかなアニメーションやユーザの操作が中断される可能性があります。現在のところ、Webの作者が画像を非同期にデコードしたいと指定する方法はないため、UIのストールを避けることができないシナリオがあります。

この問題を解決するために、画像要素の「async」属性を提案しました。この属性は、作成者が非同期デコードを要求したことをUAに知らせるヒントです。これは、「load」イベントが発火した後にUAが画像をペイントし、画像がデコードし終える前は、UAはイメージをペイントしないことを許可することを意味します(デコードでブロックするのではなく)。

デコードされた画像フレームが利用可能であるときに作成者に通知するために、画像要素に新しいイベント「ready」を発火させることを提案しました。これにより、完全にデコードされた画像を必要とする、UIストールの影響を受けやすいコンテンツの作成者は、画像を表示する処理を実行する前に「ready」イベントを待機できるようになります(CSSトランジションなど)。

アニメーションGIFなど、フレームを繰り返しデコードする画像もあります。さらに、UAはデコードされたフレームを、再デコードを必要とする静止画像用に破棄することができます。これらの場合、「ready」イベントは、フレームが最初に表示可能になったときに一度だけ発生することを提案します。

問題:
「非同期」は現在、非同期読込を暗示するために使用されていますが、イメージに対してもこの意味で使用することができます。新しい属性には「asyncDecode」のような名前を付けます。

これは、CSSイメージではなく、イメージエレメントの問題を解決するだけです。CSSイメージの非同期デコードを可能にするCSSプロパティを追加し、それが適用されるエレメントでイベントを発生させることができます。

Webページの表示速度を高速化するには、メインスレッドがブロックされない、割り込みされない事が重要です。
メインスレッドの計算処理に割り当てられたCPUに、その仕事を専念させるのです。
画像のデコード処理は、バックグラウンド処理で済ませ、ブラウザが画像以外のコンテンツを準備し終えた後に、画像を入れて更新します。

loading="lazy"

loading属性は、画像やiframeの読込処理に関しての指示をユーザエージェントに明示する属性です。
loading="lazy"と指定することで、ブラウザの非表示領域の画像やiframeの読込処理を遅延読込させます。

記述の仕方としては、以下のようになります。


<img class="hoge" loading="lazy" src="/images/hage.jpg" alt="hage">
loading属性の値と意味
意味
lazyいくつかの条件が合致するまで、画像やiframeを遅延読込させる
eager画像やiframeを即座に読み込ませる(デフォルト)

ブラウザの対応状況

HTML Living Standardの仕様として標準化されていて、Chrome、Edge、Opera、Firefoxで対応していますがSafariは未だに対応していません。

Can I Use...でのloading属性のブラウザ対応表

プロファイリングの比較

実際にどのように処理されるのか、Chrome Developer Toolsで確認しましょう。
Spelldataのトップページで検証しました。
SpelldataのWebサイトは、HTTP/1.1+Keep-aliveで配信しており、HTML、CSS、JavaScript、画像のGzip圧縮はしていません。

decoding="async"

decoding="async"で画像処理したページ

Chrome Developer Toolsでプロファイリングすると、画像の読込はメインスレッドの処理には出ていません。
HTMLのパース処理が素早く行われ、その後にCSSのパース処理が行われます。
HTMLのパース直後から画像のダウンロードが実行されているのが特徴的です。

処理の順番は以下の通りです。

  1. FP(First Paint)=FCP(First Contentful Paint)
  2. DCL(DOMContentLoaded)
  3. LCP(Large Contentful Paint)
  4. L(oad)

loading="lazy"

loading="lazy"で画像処理したページ

Chrome Developer Toolsでプロファイリングすると、decoding="async"と同様に、画像の読込はメインスレッドの処理には出ていません。
HTMLのパース処理が素早く行われ、その後にCSSとJavaScriptのダウンロード、パース処理が行われます。
HTML、CSS、JavaScriptのダウンロード・パース後から画像のダウンロードが実行されているのが特徴的です。

処理の順番は以下の通りです。

  1. L(oad)=DCL(DOMContentLoaded)
  2. FP(First Paint)=FCP(First Contentful Paint)
  3. LCP(Large Contentful Paint)

decoding="async"とloading="lazy"を併記するとどうなる?

decoding="async"とloading="lazy"は、「如何にメインスレッドの処理を妨げないか?」を違う思想で実現したものです。
従って、その仕組み上、併記しても両立はしません。
現状は、併記すると、Chromeにおいては、loading="lazy"の方が優先されます。

実装上の注意点

loading="lazy"は、Loadが先に来きますが、FPやLCPが後ろに来るので、JavaScriptなどを使った場合の処理の順番を考慮する必要があります。
decoding="async"は、FP→LCP→Loadと、従来どおりの処理の順番になるので、それほど気難しく考える必要はありません。
しかし、画像の読み込みの際に、JavaScriptの処理などが依存関係を持つと、非同期処理はされないので、その点の注意が必要です。

まとめ