重要なアセットをプリロードして読み込み速度を改善する

ウェブページを開くと、ブラウザはサーバーに HTML ドキュメントをリクエストし、そのコンテンツを解析し、参照されているリソースに対して個別のリクエストを送信します。デベロッパーは、ページに必要なすべてのリソースと、その中で最も重要なリソースをすでに把握しています。その知識を活用して、重要なリソースを事前にリクエストし、読み込みプロセスをスピードアップできます。この記事では、<link rel="preload"> を使用してこれを実現する方法について説明します。

プリロードは、通常はブラウザで遅れて検出されるリソースに最適です。

Chrome DevTools の [Network] パネルのスクリーンショット。
この例では、Pacifico フォントが @font-face ルールを使用してスタイルシートで定義されています。ブラウザは、スタイルシートのダウンロードと解析が完了した後にのみ、フォント ファイルを読み込みます。

特定のリソースをプリロードすると、現在のページにとって重要であることが確実であるため、ブラウザが検出するよりも早く取得したいとブラウザに伝えることになります。

プリロードを適用した後の Chrome DevTools の [Network] パネルのスクリーンショット。
この例では、Pacifico フォントがプリロードされているため、ダウンロードはスタイルシートと同時に行われます。

クリティカル リクエスト チェーンは、ブラウザによって優先され、フェッチされるリソースの順序を表します。Lighthouse では、このチェーンの 3 つ目のレベルにあるアセットが遅延検出として識別されます。主要なリクエストをプリロードする監査を使用して、プリロードするリソースを特定できます。

Lighthouse のプリロード キー リクエストの監査。

リソースをプリロードするには、rel="preload" を含む <link> タグを HTML ドキュメントのヘッドに追加します。

<link rel="preload" as="script" href="critical.js">

ブラウザはプリロードされたリソースをキャッシュに保存し、必要に応じてすぐに使用できるようにします。(スクリプトの実行やスタイルシートの適用は行いません)。

リソースヒント(preconnectprefetch など)は、ブラウザの判断で実行されます。一方、preload はブラウザに必須です。最新のブラウザはすでにリソースの優先順位付けに長けています。そのため、preload は慎重に使用し、最も重要なリソースのみをプリロードすることが重要です。

未使用のプリロードは、load イベントの約 3 秒後に Chrome のコンソールに警告をトリガーします。

未使用のプリロード リソースに関する Chrome DevTools コンソールの警告。

ユースケース

CSS で定義されたリソースのプリロード

@font-face ルールで定義されたフォントや CSS ファイルで定義された背景画像は、ブラウザがそれらの CSS ファイルをダウンロードして解析するまで検出されません。これらのリソースをプリロードすると、CSS ファイルのダウンロード前にフェッチされます。

CSS ファイルのプリロード

クリティカル CSS アプローチを使用している場合は、CSS を 2 つの部分に分割します。スクロールせずに見える範囲のコンテンツのレンダリングに必要なクリティカル CSS は、ドキュメントの <head> にインライン化され、重要でない CSS は通常 JavaScript で遅延読み込みされます。重要でない CSS を読み込む前に JavaScript が実行されるのを待つと、ユーザーがスクロールしたときのレンダリングが遅延することがあるため、<link rel="preload"> を使用してダウンロードを早めに開始することをおすすめします。

JavaScript ファイルのプリロード

ブラウザはプリロードされたファイルを実行しないため、プリロードは取得と実行を分離するのに役立ち、Time to Interactive などの指標を改善できます。プリロードが最適に機能するのは、JavaScript バンドルを分割して重要なチャンクのみをプリロードする場合です。

rel=preload の実装方法

preload を実装する最も簡単な方法は、ドキュメントの <head><link> タグを追加することです。

<head>
  <link rel="preload" as="script" href="critical.js">
</head>

as 属性を指定すると、ブラウザはプリフェッチされたリソースの優先度をそのタイプに応じて設定し、適切なヘッダーを設定し、リソースがキャッシュにすでに存在するかどうかを判断できます。この属性の有効な値は、scriptstylefontimageその他です。

フォントなどの一部のリソースは、匿名モードで読み込まれます。そのような場合は、crossorigin 属性を preload に設定する必要があります。

<link rel="preload" href="ComicSans.woff2" as="font" type="font/woff2" crossorigin>

<link> 要素には、リンクされたリソースの MIME タイプを含む type 属性も使用できます。ブラウザは type 属性の値を使用して、ファイル形式がサポートされている場合にのみリソースがプリロードされるようにします。ブラウザが指定されたリソースタイプをサポートしていない場合、<link rel="preload"> は無視されます。

また、Link HTTP ヘッダーを使用して、任意のタイプのリソースをプリロードすることもできます。

Link: </css/style.css>; rel="preload"; as="style"

HTTP ヘッダーで preload を指定すると、ブラウザがドキュメントを解析して検出する必要がなくなるため、場合によってはパフォーマンスがわずかに向上します。

webpack を使用して JavaScript モジュールをプリロードする

アプリケーションのビルドファイルを作成するモジュール バンドラを使用している場合は、プリロード タグの挿入がサポートされているかどうかを確認する必要があります。webpack バージョン 4.6.0 以降では、import() 内でマジック コメントを使用してプリロードがサポートされています。

import(_/* webpackPreload: true */_ "CriticalChunk")

古いバージョンの webpack を使用している場合は、preload-webpack-plugin などのサードパーティ プラグインを使用してください。

プリロードが Core Web Vitals に与える影響

プリロードは、読み込み速度に影響する強力なパフォーマンス最適化です。こうした最適化により、サイトの Core Web Vitals が変化する可能性があるため、注意が必要です。

Largest Contentful Paint(LCP)

フォントと画像の場合、画像ノードとテキストノードの両方が LCP の候補になるため、プリロードは Largest Contentful Paint(LCP)に大きな効果をもたらします。ウェブフォントを使用してレンダリングされるヘッダー画像や長いテキストは、適切に配置されたプリロード ヒントによって大幅なメリットが得られます。これらの重要なコンテンツをユーザーに迅速に提供できる機会がある場合は、プリロード ヒントを使用する必要があります。

ただし、プリロードやその他の最適化に関しては注意が必要です。特に、リソースを過剰にプリロードしないでください。優先されるリソースが多すぎると、実質的にどのリソースも優先されなくなります。過剰なプリロード ヒントは、帯域幅の競合が顕著になる低速ネットワークで特に悪影響を及ぼします。

代わりに、適切に配置されたプリロードが効果を発揮するとわかっている、価値の高いリソースに重点を置きます。フォントをプリロードする際は、WOFF 2.0 形式でフォントを配信して、リソースの読み込み時間をできるだけ短くしてください。WOFF 2.0 は優れたブラウザ サポートがあるため、WOFF 1.0 や TrueType(TTF)などの古い形式を使用すると、LCP 候補がテキストノードである場合に LCP が遅延します。

LCP と JavaScript に関しては、ブラウザのプリロード スキャナが適切に機能するように、サーバーから完全なマークアップを送信する必要があります。マークアップのレンダリングに JavaScript に完全に依存し、サーバー レンダリングされた HTML を送信できないエクスペリエンスを提供している場合は、ブラウザのプリロード スキャナが対応できない部分に介入して、JavaScript の読み込みと実行が完了したときにのみ検出可能なリソースをプリロードすることをおすすめします。

Cumulative Layout Shift(CLS)

累積レイアウト シフト(CLS)は、ウェブフォントに関する特に重要な指標です。CLS は、font-display CSS プロパティを使用してフォント読み込み方法を管理するウェブフォントと密接に関連しています。ウェブフォント関連のレイアウト シフトを最小限に抑えるには、次の方法を検討してください。

  1. font-display にデフォルトの block 値を使用しながらフォントをプリロードします。これは微妙なバランスです。フォールバックなしでフォントの表示をブロックすることは、ユーザー エクスペリエンスの問題と見なされる可能性があります。一方、font-display: block; でフォントを読み込むと、ウェブフォント関連のレイアウトのずれが解消されます。一方で、ウェブフォントがユーザー エクスペリエンスに不可欠な場合は、できるだけ早く読み込みを完了する必要があります。プリロードと font-display: block; を組み合わせることは、妥協案として許容できるかもしれません。
  2. font-displayfallback 値を使用している間、フォントをプリロードする。fallback は、ブロック期間が非常に短い点で、swapblock の妥協点です。
  3. プリロードなしで font-displayoptional 値を使用します。ウェブフォントがユーザー エクスペリエンスにとって重要ではないものの、大量のページテキストをレンダリングするために使用されている場合は、optional 値の使用を検討してください。不利な状況では、optional はフォールバック フォントでページテキストを表示し、次のナビゲーション用にフォントをバックグラウンドで読み込みます。このような状況では、システム フォントがすぐにレンダリングされ、その後のページ読み込みではレイアウト シフトなしでフォントがすぐに読み込まれるため、CLS が改善されます。

ウェブフォントに関しては、CLS の最適化が難しい指標です。いつものようにラボでテストを行いますが、フォント読み込み戦略によって CLS が改善されるか悪化するかを判断するには、フィールドデータを信頼してください。

Interaction to Next Paint(INP)

Interaction to Next Paint は、ユーザー入力に対する応答性を測定する指標です。ウェブ上のインタラクティビティの大部分は JavaScript によって駆動されるため、重要なインタラクションを可能にする JavaScript をプリロードすると、ページの INP を低く抑えることができます。ただし、起動時に JavaScript を読み込みすぎると、リソースの競合により帯域幅が圧迫され、意図しない悪影響が生じる可能性があります。

また、コード分割の方法にも注意が必要です。Code Splitting は、起動時に読み込まれる JavaScript の量を減らすための優れた最適化手法ですが、インタラクションの開始時に読み込まれる JavaScript に依存している場合、インタラクションの遅延が発生する可能性があります。この問題を補正するには、ユーザーの意図を調べ、インタラクションが発生する前に、必要な JavaScript のチャンクのプリロードを挿入する必要があります。たとえば、フォーム内のいずれかのフィールドにフォーカスが当たったときに、フォームの内容の検証に必要な JavaScript をプリロードできます。

まとめ

ページの速度を改善するには、ブラウザで遅れて検出される重要なリソースをプリロードします。すべてをプリロードするのは逆効果になるため、preload は控えめに使用し、実際の影響を測定してください。