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

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

プリロードの仕組み

プリロードは、ブラウザで後から検出されることが多いリソースに最適です。

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

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

<ph type="x-smartling-placeholder">
</ph> プリロードを適用した後の 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> 要素には type 属性も指定できます。この属性には、リンクされたリソースの MIME タイプが含まれます。ブラウザは 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 は優れたブラウザ サポートを備えているため、LCP 候補がテキストノードの場合、WOFF 1.0 や TrueType(TTF)などの古い形式を使用すると LCP が遅延します。

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

Cumulative Layout Shift(CLS)

Cumulative Layout Shift(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 をプリロードしすぎると、過剰な数のリソースが帯域幅を奪い合う場合、予期せぬ悪影響を及ぼす可能性があるので注意してください。

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

まとめ

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