リソース優先度の微調整：fetchpriority の活用

[![表紙](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967621/9a946540-0530-4b29-b2a7-fd645cfc98d5.png)](https://leapcell.io/?lc_t=q_fetchpriority)

## 背景

プリロードは、ウェブページが完全に読み込まれる前に、主要なコンテンツを先に読み込むことで、ユーザーにより良い体験を提供し、待ち時間を短縮する技術です。しかし、一部のケースでは、プリロードされたリソースの優先度をさらに細かく調整する必要があります。プリロードによるリソースの優先度設定だけでは十分でない場合、補助的な手段として `fetchpriority` を使用することができます。

## 目的

- リソースの取得優先度に影響を与えること
- プリロード後のリソースの読み込み順序を補完すること

優先度は、ブラウザにおけるリソースの相対的な優先順位を示し、最適な読み込みを実現し、ウェブユーザーの体験を向上させるのに役立ちます。

## リソースの優先度

ブラウザがウェブページの解析を開始し、画像、JavaScript、CSS などのリソースをダウンロードするとき、各リソースには `fetch priority` というダウンロード優先度を表すフラグが割り当てられます。

リソースのダウンロード順序はこの優先度フラグによって決まり、優先度の計算ロジックは多くの要因の影響を受けます：

- CSS、フォント、スクリプト、画像、サードパーティのリソースにはそれぞれ異なる優先度が割り当てられる。
- リソースが HTML ドキュメント内でどこに配置されているか（順序や位置）も優先度に影響を与える。
- プリロードのヒントを活用すると、ブラウザがリソースをより早く発見し、ページの読み込み前に取得できるため、優先度に影響を与える。
- `async` や `defer` を設定したスクリプトは、優先度の計算に影響を及ぼす。

ブラウザは通常、発見した順にリソースをダウンロードします。Chrome DevTools の Network タブでは、それぞれのリソースに割り当てられた優先度を確認できます。

![Screenshot](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967621/7b7258bc-1f7f-4a55-b515-1543e219f02f.png)

しかし、デフォルトのダウンロード優先度が常に最適とは限りません。

## 優先度のヒントが必要な場面

技術的な適用シナリオ：

- 複数のファーストビュー画像が存在するが、それらすべてが同じ優先度を持つ必要はない場合。例えば、画像カルーセルでは最初に表示される画像のみが高優先度であるべきで、他の画像は優先度を下げるべき。
- 表示エリア内の画像は低優先度と判断されるが、レイアウト完了後に Chrome がそれを認識し、自動的に優先度を上げることがある。この場合、画像の読み込みが遅延する可能性があるため、優先度のヒントを設定することで、最初から高優先度で読み込ませることができる。
- `async` や `defer` を指定したスクリプトは、ブラウザによって「低優先度」に設定される。しかし、非同期ダウンロードを維持しつつも、重要なスクリプトの優先度を向上させたい場合がある。
- CSS やフォントは通常高優先度に設定されるが、すべてのリソースが等しく重要なわけではない。一部のリソースの優先度を下げるために優先度のヒントを活用できる。
- `fetch()` を使用して非同期にリソースやデータを取得する際、ブラウザはデフォルトで高優先度を割り当てる。しかし、すべてのリクエストが高優先度である必要はなく、異なる優先度のヒントを使用できる。例えば、バックグラウンドの API 呼び出しは低優先度に、インタラクティブな API 呼び出しは高優先度に設定可能。
- ネットワーク帯域が限られている環境では、優先度設定の恩恵がより顕著に表れる。

## `fetchpriority` 属性

`fetchpriority` 属性には、以下の 3 つの値を指定できます：

- `high`：このリソースの優先度を高くし、ブラウザに最優先で読み込ませる。
- `low`：このリソースの優先度を低くし、ブラウザが後回しにするよう指示する。
- `auto`：ブラウザのデフォルトの優先度を適用する。

使用例：

```html
<!-- この画像は表示エリア内にあるが、優先度は低くする -->
<img src="/images/in_viewport_but_not_important.svg" fetchpriority="low" alt="重要ではない画像" />

<!-- リソースを早めに取得したいが、優先度は低く設定 -->
<link rel="preload" href="/js/script.js" as="script" fetchpriority="low" />

<script>
  fetch('https://example.com/', { priority: 'low' }).then((data) => {
    // 低優先度でリソースを取得
  });
</script>

<!-- この iframe 内のコンテンツの読み込み優先度を低くする -->
<iframe src="https://example.com" width="600" height="400" fetchpriority="low"></iframe>
```

### LCP 画像の優先度を上げる

Google Flights のようなページでは、LCP（Largest Contentful Paint）スコアに影響を与える主な要因が背景画像であることがあります。その場合、`fetchpriority` 属性を使用して、画像の優先度を上げることが可能です。

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

優先度を `high` に設定すると、LCP が **2.6 秒 → 1.9 秒** に改善しました。

### ファーストビューの画像の優先度を下げる

カルーセルの後続の画像の優先度を `low` に設定することで、最初に表示される画像の読み込みを優先できます。

```html
<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>
```

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

重要でないプリロードリソースの優先度を下げ、他の重要なリソースとの競合を避ける：

```html
<!-- 重要なスクリプトの優先度はそのまま -->
<link rel="preload" as="script" href="critical-script.js" />

<!-- 非クリティカルなスクリプトの優先度を下げる -->
<link rel="preload" href="/js/script.js" as="script" fetchpriority="low" />

<!-- 他のリソースをブロックせずに CSS をプリロード -->
<link
  rel="preload"
  as="style"
  href="theme.css"
  fetchpriority="low"
  onload="this.rel='stylesheet'"
/>
```

### スクリプトの優先度を調整する

ページ上に必要なインタラクティブなスクリプトがあるものの、他のリソースをブロックする必要がない場合、それらを高優先度に設定しつつ、非同期で読み込むことができます。

```html
<script src="async_but_important.js" async importance="high"></script>
```

一方、特定の DOM ノードに依存するスクリプトは非同期読み込み (`async`) には適していません。しかし、それらがファーストビューのレンダリングに必須でない場合、優先度を下げることが可能です。

```html
<script src="blocking_but_unimportant.js" importance="low"></script>
```

### `fetch` リクエストの優先度を調整する

ブラウザはデフォルトで `fetch` リクエストを高優先度で処理しますが、不要な高優先度リクエストを防ぐために優先度を調整することができます。

```js
// ユーザー認証データ (デフォルトで高優先度)
let authenticate = await fetch('/user');

// 推奨コンテンツの取得 (低優先度を指定)
let suggestedContent = await fetch('/content/suggested', { priority: 'low' });
```

## 注意点

優先度のヒントは特定のケースでパフォーマンスを向上させることができますが、以下の点に注意が必要です：

- `fetchpriority` は **あくまでヒント** であり、ブラウザが必ずしも指示どおりに従うわけではありません。状況によっては、ブラウザがリソースの優先度を独自に調整する可能性があります。

- `fetchpriority` は **プリロードとは異なる** 概念です。

  - プリロード (`preload`) は強制的なリソースの取得であり、ヒントではない。
  - プリロードはリソースの取得状況をより簡単に観測・測定できる。

- `fetchpriority` はプリロードを補完するものであり、優先度の粒度を細かく制御することができます。たとえば、LCP 画像を `preload` で指定済みの場合、`fetchpriority="high"` を追加しても大きな効果はないかもしれません。しかし、プリロードが他の低優先度のリソースの後になっている場合は、`fetchpriority="high"` にすることで LCP を改善できます。特に、LCP 画像が **CSS の背景画像** の場合は、`fetchpriority="high"` を適用すると効果が期待できます。

- **CDN による HTTP/2 の優先度制御にはばらつきがある**  
  [CDN による HTTP/2 優先度管理の問題](https://github.com/andydavies/http2-prioritization-issues#cdns--cloud-hosting-services) でも指摘されているように、すべての CDN やクラウドサービスが同じ優先度の実装をしているわけではありません。

> ブラウザが優先度のヒントを使用してリクエストの優先度を決定しても、CDN がその優先度を尊重しない場合、期待通りの順序でリソースが読み込まれない可能性があります。

## 補足

### `importance` 属性

`importance` 属性は、もともと 2018 年に Chrome で試験的に導入された優先度設定の仕組みです。その後、2021 年に再び `importance` 属性が試験されましたが、Web 標準化のプロセスの中で、HTML では `fetchpriority`、JavaScript では `priority` という名称に変更されました。

---

### [私たちはLeapcell、バックエンド・プロジェクトのホスティングの最適解です。](https://leapcell.io/?lc_t=q_fetchpriority)

[![Leapcell](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967621/d95c1cb2-5a3c-4118-9cba-cc085cf3793a.png)](https://leapcell.io/?lc_t=q_fetchpriority)

[Leapcell](https://leapcell.io/?lc_t=q_fetchpriority)は、Webホスティング、非同期タスク、Redis向けの次世代サーバーレスプラットフォームです：

**複数言語サポート**

- Node.js、Python、Go、Rustで開発できます。

**無制限のプロジェクトデプロイ**

- 使用量に応じて料金を支払い、リクエストがなければ料金は発生しません。

**比類のないコスト効率**

- 使用量に応じた支払い、アイドル時間は課金されません。
- 例: $25で6.94Mリクエスト、平均応答時間60ms。

**洗練された開発者体験**

- 直感的なUIで簡単に設定できます。
- 完全自動化されたCI/CDパイプラインとGitOps統合。
- 実行可能なインサイトのためのリアルタイムのメトリクスとログ。

**簡単なスケーラビリティと高パフォーマンス**

- 高い同時実行性を容易に処理するためのオートスケーリング。
- ゼロ運用オーバーヘッド — 構築に集中できます。

[ドキュメントで詳細を確認！](https://docs.leapcell.io/)

[![Try Leapcell](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967621/16bfb729-0d8d-4158-9428-98aacd779867.png)](https://leapcell.io/?lc_t=q_fetchpriority)

Xでフォローする：[@LeapcellHQ](https://x.com/LeapcellHQ)

---

[ブログでこの記事を読む](https://leapcell.io/blog/optimizing-resource-loading-with-fetchpriority)