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

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

プリロードの仕組み

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

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

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

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

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

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

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

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

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

リソースヒント(preconnectprefetch など)は、ブラウザが適切と判断したタイミングで実行されます。一方、preload はブラウザで必須です。最新のブラウザはリソースの優先順位付けをかなりうまく行えるため、preload は控えめに使用し、最も重要なリソースのみをプリロードすることが重要です。

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

使用されていないプリロードされたリソースに関する Chrome DevTools Console の警告。

ユースケース

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

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

CSS ファイルのプリロード

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

JavaScript ファイルのプリロード

ブラウザはプリロードされたファイルを実行しないため、プリロードはフェッチを実行から分離するのに役立ち、インタラクティブになるまでの時間などの指標を改善できます。プリロードは、JavaScript バンドルを分割し、重要なチャンクのみをプリロードする場合に最適に機能します。

rel=preload の実装方法

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

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

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

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

<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)

フォントと画像に関しては、プリロードは Largest Contentful Paint(LCP)に大きな影響を与えます。画像とテキスト ノードの両方が 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 値を使用しながらフォントをプリロードします。fallbackswapblock の妥協点であり、ブロック期間が非常に短くなっています。
  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 は控えめに使用し、実際の効果を測定してください。