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

ウェブページを開くと、ブラウザはサーバーに 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 は慎重に使用し、最も重要なリソースのみをプリロードすることが重要です。

プリロードが使用されていないと、Chrome でコンソール警告がトリガーされます(load イベントから約 3 秒後)。

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

使用例

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

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

CSS ファイルのプリロード

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

JavaScript ファイルのプリロード

ブラウザではプリロードされたファイルが実行されないため、プリロードは実行とフェッチを分けるのに役立ちます。これにより、操作可能になるまでの時間などの指標を改善できます。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)

プリロードは、フォントと画像の 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 値を使用しながらフォントをプリロードするようにしました。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 の使用は控えめにして、実際の影響を測定してください。