Fetch Priority API によるリソース読み込みの最適化

Fetch Priority API は、ブラウザに対するリソースの相対的な優先度を示します。読み込みを最適化し、Core Web Vitals を改善できます。

Addy Osmani
Addy Osmani
Leena Sohoni
Leena Sohoni
Patrick Meenan
Patrick Meenan

対応ブラウザ

  • 102
  • 102
  • x
  • 17.2

ソース

ブラウザはウェブページを解析し、画像、スクリプト、CSS などのリソースの検出とダウンロードを開始すると、最適な順序でリソースをダウンロードできるように、フェッチ priority を割り当てます。これらの優先順位は、リソースの種類とドキュメント内の場所によって異なります。たとえば、ビューポート内の画像の優先度は High で、<head><link> による早期読み込みのレンダリング ブロック CSS の優先度は Very High にすることができます。ブラウザは、適切な優先度を割り当てるのが得意ですが、すべてのケースで最適であるとは限りません。

この記事では、Fetch Priority API と fetchpriority HTML 属性について説明します。この属性を使用すると、リソースの相対的な優先度(high または low)をヒントにすることができます。Fetch Priority は、Core Web Vitals の最適化に役立ちます。

まとめ

「Fetch Priority」は、次のような場面で役立ちます。

  • 画像要素に fetchpriority="high" を指定することで、LCP 画像の優先度を上げます。これにより、LCP がより早く発生します。
  • 一般的に使用されている現在のハッキングよりも優れたセマンティクスを使用して、async スクリプトの優先度を上げます(async スクリプトに <link rel="preload"> を挿入します)。
  • 後期スクリプトの優先順位を下げて、画像の順序付けを改善します。
Google フライトのホームページで 2 つのテストを比較したフィルムストリップ ビュー。一番下では、[取得の優先度] を使用してヒーロー画像の優先度を上げることで、LCP を 0.7 秒削減しています。
Google フライトのテストで、Largest Contentful Paint を 2.6 秒から 1.9 秒に改善する Priority をフェッチ

これまで、デベロッパーはプリロード事前接続を使用して、リソースの優先度にある程度の影響を及ぼしてきましたが、限定的でした。フェッチ優先度はこれらのリソースヒントを補完しますが、すべての位置付けを理解することが重要です。プリロードを使用すると、重要なリソースが自然に検出される前に早めに読み込むようブラウザに伝えることができます。これは、スタイルシートに含まれるフォント、背景画像、スクリプトから読み込まれるリソースなど、見つけにくいリソースに対して特に便利です。事前接続は、クロスオリジン サーバーへの接続をウォームアップし、Time-to-first-byteなどの指標の改善に役立ちます。また、送信元はわかっているが、必要になるリソースの正確な URL が必ずしもわからない場合に便利です。

取得優先度はマークアップ ベースのシグナル(fetchpriority 属性で使用可能)で、デベロッパーはこれを使用して特定のリソースの相対的な優先度を指定できます。また、JavaScript や Fetch APIpriority プロパティを使用してこれらのヒントを使用し、データに対して行われるリソース取得の優先度に影響を与えることもできます。フェッチ優先度はプリロードを補完することもできます。Largest Contentful Paint 画像を取得します。プリロードしても、優先度は低くなります。優先度の低い他のリソースによって差し戻された場合は、優先度フェッチを使用すると、画像が読み込まれるまでの時間を大幅に短縮できます。

フェッチの優先度は Chrome 101 以降で利用可能です。

リソースの優先度

リソースのダウンロード順序は、ページ上の各リソースにブラウザに割り当てられた優先度によって異なります。優先度の計算ロジックにはさまざまな要因が影響します。次に例を示します。

  • CSS、フォント、スクリプト、画像、サードパーティのリソースには、それぞれ異なる優先度が割り当てられます。
  • ドキュメント内でリソースを参照する場所や順序も、リソースの優先度に影響します。
  • preload リソースヒントを使用すると、ブラウザがリソースをより迅速に検出できるため、ドキュメントが読み込まれる前にリソースを読み込むことができ、優先度にも影響します。
  • async または defer スクリプトの優先度計算の変更。

次の表では、このような要因を考慮して、Chrome での現在のリソースの優先度と順序を示しています。

  レイアウト ブロック フェーズで読み込む レイアウト ブロック フェーズで一度に 1 つずつ読み込む
点滅
優先
VeryHigh VeryLow
DevTools
優先
最高 最低
メインリソース
CSS(早期**) CSS(遅延**) CSS(メディアの不一致***)
スクリプト(早期** またはプリロード スキャナからではない) スクリプト(遅延**) スクリプト(非同期)
Font フォント(rel=preload)
インポート
画像(ビューポート内) 画像(最初の 5 個の画像 > 10,000px2) 画像
メディア(動画/音声)
プリフェッチ
XSL
XHR(同期) XHR/フェッチ*(非同期)

ブラウザは、同じ優先度でリソースを検出順にダウンロードします。ページの読み込み時にさまざまなリソースに割り当てられている優先度は、Chrome デベロッパー ツールの [ネットワーク] タブで確認できます。(表の見出しを右クリックして、優先度列が含まれていることを確認します)。

Chrome の DevTools の [Network] タブに表示されたアセットのスクリーンショット。列は、左から右に、名前、ステータス、タイプ、イニシエータ、サイズ、時間、優先度を示しています。
BBC ニュース詳細ページにおけるリソース type = "font" の優先度
Chrome の DevTools の [Network] タブに表示されたアセットのスクリーンショット。列は、左から右に、名前、ステータス、タイプ、イニシエータ、サイズ、時間、優先度を示しています。
BBC ニュース詳細ページにおけるリソースタイプ =「script」の優先度

優先度が変更される場合は、[大きなリクエスト行] の設定を使用して、初期優先度と最終優先度の両方を表示できます。[大きなリクエスト行] の設定に関係なく、ツールチップにも同じ内容が表示されます。

Chrome の DevTools の [Network] タブに表示されたアセットのスクリーンショット。[大きなリクエスト行] 設定がオンになっており、[優先度] 列に優先度が「高」で、その下に異なる初期優先度「中」の画像が表示されます。ツールチップに同じ内容が表示されます。
DevTools で初期優先度と最終優先度の両方を確認する

フェッチ優先度が必要になるのはどのような場合ですか。

ブラウザの優先順位付けロジックの知識に基づいて、ダウンロードの順序を微調整するためのいくつかの既存のノブを利用できます。Google Chat では

  1. ダウンロードする順序に応じて、<script><link> などのリソースタグを配置します。通常、優先度が同じリソースは、検出された順に読み込まれます。
  2. 必要なリソースを早期にダウンロードするには、preload のリソースヒントを使用します。これは、ブラウザが早い段階で検出するのが難しいリソースの場合は特に重要です。
  3. async または defer を使用すると、他のリソースをブロックせずにスクリプトをダウンロードします。
  4. スクロールしなければ見えない範囲のコンテンツを遅延読み込みして、ブラウザがより重要なスクロールせずに見える範囲のリソースに利用可能な帯域幅を使用できるようにする。

これらの手法は、ブラウザの優先度の計算を制御し、パフォーマンスと Core Web Vitals を改善するうえで役立ちます。たとえば、重要な背景画像がプリロードされている場合、その画像をはるかに早い段階で検出できるため、Largest Contentful Paint(LCP)が改善されます。

場合によっては、これらのハンドルではアプリケーションに最適なリソースに優先順位を付けるには不十分な場合があります。フェッチ優先度が役立つシナリオには、次のようなものがあります。

  1. スクロールせずに見える範囲の画像は複数ありますが、すべての優先度を同じにする必要はありません。たとえば、画像カルーセルでは、最初に表示される画像だけに他の画像よりも高い優先度を割り当てる必要があります。
  2. ビューポート内のヒーロー画像は通常、優先度を「低」から開始します(Chrome 117 の変更では、大きい画像の最初の 5 つが「中」に設定されますが、ここにはヒーロー画像が含まれるとは限りません)。レイアウトが完成すると、Chrome ではこれらの要素がビューポートにあると認識され、優先度が高くなります。これにより、通常、画像の読み込みに大幅な遅延が発生します。マークアップでフェッチ優先度を指定すると、画像の優先度を「高」で開始し、ずっと早く読み込みを開始できます。

    なお、CSS の背景として含まれる LCP 画像を早期に検出するには、プリロードが必要です。また、プリロードに fetchpriority='high' を含めることで取得優先度と組み合わせることができます。そうでない場合、画像の優先度は「低」または「中」になります。
  3. スクリプトを async または defer として宣言すると、ブラウザはスクリプトを非同期で読み込むようになります。ただし、前の表に示すように、これらのスクリプトにも優先度「低」が割り当てられます。特にユーザー エクスペリエンスに不可欠なスクリプトについては、非同期ダウンロードを確実に行いながら、優先度を上げることができます。
  4. JavaScript fetch() API を使用すると、リソースやデータを非同期で取得できます。フェッチには、ブラウザによって「高」優先度が割り当てられます。一部のフェッチを優先度「高」で実行したくない場合、別のフェッチの優先度を使用したほうがよい場合があります。これは、バックグラウンドで API 呼び出しを実行し、それをオートコンプリートなど、ユーザー入力に応答する API 呼び出しと混在させる場合に便利です。バックグラウンド API 呼び出しの優先度は「低」、インタラクティブ API 呼び出しの優先度は「高」とします。
  5. ブラウザは CSS とフォントに「高」の優先度を割り当てますが、そのようなリソースすべてが LCP で同等に重要ではなく、必要でない可能性があります。フェッチの優先度を使用すると、一部のリソースの優先度を下げることができます。

fetchpriority 属性

取得の優先度を指定するには、fetchpriority HTML 属性を使用します。この属性は、link タグ、img タグ、script タグで使用できます。この属性を使用すると、サポートされているタグを使用してダウンロードされる際の、CSS、フォント、スクリプト、画像などのリソースタイプの優先度を指定できます。fetchpriority 属性は、次の 3 つの値のいずれかを受け入れます。

  • high: リソースの優先度は高いとみなし、ブラウザのヒューリスティックがそれを妨げる場合を除き、ブラウザがリソースを優先させたいとします。
  • low: リソースの優先度は低いとみなし、ヒューリスティックが許す限りブラウザでの優先度を下げます。
  • auto: これはデフォルト値で、特に指定がなければ、ブラウザが適切な優先度を決定します。

マークアップで fetchpriority 属性を使用する例と、スクリプトで同等の priority プロパティの例を示します。

<!-- We don't want a high priority for this above-the-fold image -->
<img src="/images/in_viewport_but_not_important.svg" fetchpriority="low" alt="I'm an unimportant image!">

<!-- We want to initiate an early fetch for a resource, but also deprioritize it -->
<link rel="preload" href="/js/script.js" as="script" fetchpriority="low">

<script>
  fetch('https://example.com/', {priority: 'low'})
  .then(data => {
    // Trigger a low priority fetch
  });
</script>

ブラウザの優先値と fetchpriority

次の図に示すように、さまざまなリソースに fetchpriority 属性を適用すると、計算された優先度を増減できます。各行の fetchpriority="auto"(◉)は、そのタイプのリソースのデフォルトの優先度を示します(Google ドキュメントとしても利用可能です)。

  レイアウト ブロック フェーズで読み込む レイアウト ブロック フェーズで一度に 1 つずつ読み込む
点滅
優先
VeryHigh VeryLow
DevTools
優先
最高 最低
メインリソース
CSS(早期**) ⬆◉
CSS(遅延**)
CSS(メディアの不一致***) ⬆*** ◉⬇
スクリプト(早期** またはプリロード スキャナからではない) ⬆◉
スクリプト(遅延**)
スクリプト(async/defer) ◉⬇
Font
フォント(rel=preload) ⬆◉
インポート
画像(ビューポート内 - レイアウト後) ⬆◉
画像(最初の 5 個の画像 > 10,000px2)
画像 ◉⬇
メディア(動画/音声)
XHR(同期)- サポート終了
XHR/フェッチ*(非同期) ⬆◉
プリフェッチ
XSL

fetchpriority は、相対優先度を設定します。つまり、明示的に「高」または「低」に設定するのではなく、デフォルトの優先度を適切な量だけ上げたり下げたりして、ブラウザが相対的な優先度を決定します。多くの場合は「高」や「低」ですが、必ずそうであるとは限りません。たとえば、fetchpriority="high" が指定されたクリティカルな CSS の優先度は「非常に高い」/「最高」のままで、fetchpriority="low" を使用しても「高」の優先度が維持されます。どちらの場合も、明示的に「高」または「低」に設定されることはありません。

使用例

fetchpriority 属性を使用すると、リソースを取得する優先度について追加のヒントをブラウザに提供したい場合があります。

LCP イメージの優先度を上げる

fetchpriority="high" を指定すると、LCP などの重要なイメージの優先度を高めることができます。

<img src="lcp-image.jpg" fetchpriority="high">

次の比較は、Google フライトページで LCP の背景画像が読み込まれた状態(取得優先度ありとなしの場合)を示しています。優先度を「高」に設定すると、LCP は 2.6 秒から 1.9 秒に向上しました。

Cloudflare ワーカーを使用して Google フライトのページを書き換え、[優先度をフェッチ] を使用するように実施したテスト。

スクロールせずに見える範囲の画像の優先度を下げる

fetchpriority="low" を使用すると、最初は重要でない可能性があるスクロールせずに見える範囲の画像(カルーセルなど)の優先度を下げることができます。

<ul class="carousel">
  <img src="img/carousel-1.jpg" fetchpriority="high">
  <img src="img/carousel-2.jpg" fetchpriority="low">
  <img src="img/carousel-3.jpg" fetchpriority="low">
  <img src="img/carousel-4.jpg" fetchpriority="low">
</ul>

Oodle アプリの以前のテストでは、これを使用して、読み込み時に表示されない画像の優先度を下げることができました。読み込み時間を 2 秒短縮することができました。

Oodle アプリの画像カルーセルで使用されている場合のフェッチ優先度の比較。左側では、ブラウザがカルーセル画像にデフォルトの優先順位を設定していますが、右側の例よりも画像のダウンロードと描画が約 2 秒遅くなります。つまり、最初のカルーセル画像だけに高い優先度が設定されています。

プリロード リソースの優先度を下げる

プリロードされたリソースが他の重要なリソースと競合しないようにするには、優先度を下げるヒントを提供します。この方法は、画像、スクリプト、CSS に適用できます。

<!-- Lower priority only for non-critical preloaded scripts -->
<link rel="preload" as="script" href="critical-script.js">
<link rel="preload" href="/js/script.js" as="script" fetchpriority="low">

<!-- Preload CSS without blocking other resources -->
<link rel="preload" as="style" href="theme.css" fetchpriority="low" onload="this.rel='stylesheet'">

スクリプトの優先度を再設定する

ページの一部をインタラクティブにするために必要なスクリプトは不可欠ですが、他のリソースをブロックすべきではありません。これらは、優先度の高い非同期としてマークできます。

<script src="async_but_important.js" async fetchpriority="high"></script>

特定の DOM 状態に依存しているスクリプトを非同期としてマークすることはできません。ただし、ページの下の方にある場合は、次に示すように低い優先度でダウンロードされる可能性があります。

<script src="blocking_but_unimportant.js" fetchpriority="low"></script>

重要性の低いデータ取得の優先度を下げる

ブラウザが高い優先度で fetch を実行する。同時に複数の取得が行われる可能性がある場合、重要度の高いデータの取得にはデフォルトの優先度を高くし、重要度の低いデータには優先度を低くすることができます。

// Important validation data (high by default)
let authenticate = await fetch('/user');

// Less important content data (suggested low)
let suggestedContent = await fetch('/content/suggested', {priority: 'low'});

優先度の実装メモを取得する

前述のように、フェッチ優先度を使用すると、特定のユースケースでパフォーマンスを改善できます。次の点に注意してください。

  • fetchpriority 属性はヒントであり、ディレクティブではありません。ブラウザはデベロッパーの設定を優先しようとします。また、競合が発生した場合に必要と思われる場合に、ブラウザがリソースの優先度の設定を適用する場合もあります。
  • 「取得の優先度」を「プリロード」と混同しないでください。この 2 つには以下のような違いがあります。

    • プリロードは必須のフェッチであり、ヒントではありません。
    • プリロードを使用すると、ブラウザはリソースを早期に検出できますが、引き続きデフォルトの優先度で取得されます。逆に、フェッチの優先度は見つけやすさの向上には役立ちませんが、フェッチの優先度を増減することはできます。
    • プリロードの影響を簡単にモニタリング、測定できます。

    フェッチの優先度は、優先順位の粒度を上げることで、プリロードを補完します。プリロードを LCP 画像の <head> の最初の項目の 1 つとしてすでに指定している場合は、high 取得優先度で大幅な改善が見られないことがあります。ただし、プリロードが他のリソースより後に行われた場合は、high 取得優先度によって LCP を改善できます。重要な画像が CSS の背景画像である場合は、fetchpriority = "high" を使用してプリロードする必要があります。

  • 優先順位付けによる顕著なメリットは、利用可能なネットワーク帯域幅で競合するリソースが多い環境ではより重要になります。これは、並列ダウンロードができない HTTP/1.x 接続や、低帯域幅の HTTP/2 接続でよく発生します。優先順位を付けることで、このような状況でのボトルネックを解決できます。

  • CDN では、HTTP/2 の優先度が一律に実装されるわけではありません。ブラウザがフェッチ優先度を使用して提案された優先度を伝達しても、CDN がリソースを必要な順序で再優先順位付けしない場合があります。このため、フェッチの優先度のテストが困難になります。優先度は、ブラウザ内で内部的にも、優先度設定をサポートするプロトコル(HTTP/2 と HTTP/3)でも適用されます。CDN や送信元のサポートとは無関係に、内部のブラウザの優先度を設定する場合であっても、これは使用する価値があります。これは、リソースがブラウザによってリクエストされたときに変更されることが多いためです。たとえば、ブラウザが重要な <head> アイテムを処理する間、画像などの優先度の低いリソースはリクエストされないことが多くあります。

  • 最初の設計では、フェッチ優先度をベスト プラクティスとして導入できない場合があります。これは、開発サイクルの後半に適用できる最適化です。ページ上のさまざまなリソースに割り当てられている優先度を確認できます。優先度が想定と一致しない場合は、取得優先度を導入して最適化をさらに進めることができます。

Chrome 95 以降でプリロードを使用する

「優先度を取得する」機能は Chrome 73 ~ 76 では試用可能でしたが、Chrome 95 で修正済みのプリロードによる優先度の問題により、リリースされませんでした。Chrome 95 より前では、<link rel=preload> を介して発行されたリクエストは、優先度の高いリクエストであっても、プリロード スキャナが検出する他のリクエストよりも前に常に開始されます。

Chrome 95 での修正と Fetch Priority の機能強化により、パーサーでは検出されないリソース(フォント、インポート、バックグラウンド LCP 画像)をプリロードするという本来の用途で、プリロードが使用されるようになることを願っています。preload ヒントの配置は、リソースがプリロードされるタイミングに影響します。プリロードを使用する際の重要なポイントは次のとおりです。

  • HTTP ヘッダーにプリロードを含めると、他のものより先にジャンプします。
  • 通常、プリロードは、優先度が「中」以上の場合、パーサーが取得した順序で読み込まれます。そのため、HTML の先頭にプリロードを含める場合は注意が必要です。
  • フォント プリロードは、通常、head の最後または body の始めに行うと最も効果を発揮します。
  • インポートのプリロード(動的 import() または modulepreload)は、インポートが必要なスクリプトタグの後に行う必要があります(実際のスクリプトが最初に読み込まれて解析されます)。基本的に、依存関係の読み込みをトリガーするスクリプトをスクリプトタグが読み込む場合、依存関係の <link rel=preload> が親スクリプトタグの後になるようにしてください。そうしないと、依存関係がメイン スクリプトよりも先に読み込まれる可能性があります。依存関係の読み込み中に、メイン スクリプトを適切な順序で解析/評価できます。
  • イメージのプリロードには、「低」または「中」の優先度(フェッチ優先度なし)が設定され、非同期スクリプトやその他の優先度の低いタグまたは最も低いタグに対して相対的な順序で並べる必要があります。

History

Fetch Priority は、2018 年に Chrome のオリジン トライアルとして最初にテストされ、2021 年に importance 属性を使用して再度テストされました。当時、これは優先度のヒントと呼ばれていました。その後、ウェブ標準プロセスの一環として、HTML では fetchpriority、JavaScript の Fetch API では priority にインターフェースが変更されました。混乱を避けるため、この API を「フェッチの優先度」と呼ぶようになりました。

おわりに

プリロード動作の修正や最近の Core Web Vitals と LCP への注力により、開発者はフェッチ 優先度に関心を持つ可能性が高いと思われます。現在は、目的の読み込みシーケンスを実現するための追加のノブを利用できます。