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

この Codelab では、いくつかのリソースをプリロードおよびプリフェッチすることで、次のウェブページのパフォーマンスを改善します。

アプリのスクリーンショット

測定

まず、最適化を行う前にウェブサイトのパフォーマンスを測定します。

  • サイトをプレビューするには、[アプリを表示] を押し、[全画面表示] 全画面表示 を押します。

Glitch のライブ バージョンで Lighthouse のパフォーマンス監査([Lighthouse] > [オプション] > [パフォーマンス])を実行します(Lighthouse でパフォーマンスの改善機会を見つけるも参照してください)。

Lighthouse は、遅れて取得されたリソースに対して次の失敗した監査を表示します。

Lighthouse: 主要なリクエストをプリロードする監査
  • `Ctrl+Shift+J`(Mac の場合は `Command+Option+J`)を押して、デベロッパー ツールを開きます。
  • [ネットワーク] タブをクリックします。
遅れて検出されたリソースを含むネットワーク パネル

main.css ファイルは、HTML ドキュメントに配置された Link 要素(<link>)によって取得されませんが、別の JavaScript ファイル fetch-css.jswindow.onLoad イベントの後に Link 要素を DOM に追加します。つまり、ブラウザが JS ファイルの解析と実行を完了したにのみ、ファイルが取得されます。同様に、main.css 内で指定されたウェブフォント(K2D.woff2)は、CSS ファイルのダウンロードが完了した後にのみ取得されます。

クリティカル リクエスト チェーンは、ブラウザによって優先順位が付けられ、取得されるリソースの順序を表します。このウェブページの場合、現在は次のようになっています。

├─┬ / (initial HTML file)
  └── fetch-css.js
    └── main.css
      └── K2D.woff2

CSS ファイルはリクエスト チェーンの 3 番目のレベルにあるため、Lighthouse はこれを遅延検出リソースとして特定しています。

重要なリソースをプリロードする

main.css ファイルは、ページが読み込まれるとすぐに必要になる重要なアセットです。このリソースのように、アプリケーションで遅れて取得される重要なファイルについては、リンク プリロード タグを使用して、ドキュメントの head に Link 要素を追加することで、ブラウザに早めにダウンロードするよう通知します。

このアプリケーションのプリロード タグを追加します。

<head>
  <!-- ... -->
  <link rel="preload" href="main.css" as="style">
</head>

as 属性は、取得するリソースのタイプを識別するために使用され、as="style" はスタイルシート ファイルをプリロードするために使用されます。

アプリケーションを再読み込みして、DevTools の [Network] パネルを確認します。

プリロードされたリソースを含むネットワーク パネル

ブラウザが CSS ファイルを取得する前に、その取得を担当する JavaScript の解析が完了していることに注目してください。プリロードを使用すると、ブラウザは、リソースがウェブページにとって重要であると想定して、リソースの先読みを行うことを認識します。

正しく使用しないと、使用されないリソースに対する不要なリクエストが行われ、パフォーマンスが低下する可能性があります。このアプリケーションでは、details.css はプロジェクトのルートにある別の CSS ファイルですが、別の /details route に使用されます。プリロードの誤った使用方法の例を示すため、このリソースのプリロード ヒントも追加します。

<head>
  <!-- ... -->
  <link rel="preload" href="main.css" as="style">
  <link rel="preload" href="details.css" as="style">
</head>

アプリケーションを再読み込みして、[ネットワーク] パネルを確認します。ウェブページで使用されていないにもかかわらず、details.css を取得するリクエストが行われます。

不要なプリロードを含むネットワーク パネル

プリロードされたリソースが読み込み後数秒以内にページで使用されない場合、Chrome は [コンソール] パネルに警告を表示します。

コンソールのプリロードに関する警告

この警告は、ウェブページで直ちに使用されていないプリロードされたリソースがあるかどうかを特定するための指標として使用します。これで、このページの不要なプリロード リンクを削除できます。

<head>
  <!-- ... -->
  <link rel="preload" href="main.css" as="style">
  <link rel="preload" href="details.css" as="style">
</head>

取得できるすべてのリソースタイプと、as 属性に使用する正しい値のリストについては、プリロードに関する MDN 記事を参照してください。

将来のリソースをプリフェッチする

プリフェッチは、別のナビゲーション ルートで使用されるアセットのリクエストを、現在のページに必要な他の重要なアセットよりも低い優先度で行うために使用できる、もう 1 つのブラウザ ヒントです。

このウェブサイトでは、画像をクリックすると別の details/ ルートに移動します。

ルートの詳細

別の CSS ファイル details.css には、このシンプルなページに必要なすべてのスタイルが含まれています。index.html にリンク要素を追加して、このリソースをプリフェッチします。

<head>
  <!-- ... -->
  <link rel="prefetch" href="details.css">
</head>

これによりファイルのリクエストがどのようにトリガーされるかを確認するには、DevTools で [ネットワーク] パネルを開き、[キャッシュを無効にする] オプションのチェックを外します。

Chrome DevTools でキャッシュを無効にする

アプリケーションを再読み込みし、他のすべてのファイルが取得された後に details.css に対して非常に低い優先度のリクエストが行われることを確認します。

プリフェッチされたリソースを含む [ネットワーク] パネル

DevTools を開いた状態で、ウェブサイトの画像をクリックして details ページに移動します。details.html でリンク要素を使用して details.css を取得するため、リソースに対するリクエストが想定どおりに行われます。

詳細ページのネットワーク リクエスト

DevTools で details.css ネットワーク リクエストをクリックして、詳細を表示します。ファイルがブラウザのディスク キャッシュから取得されていることがわかります。

ディスク キャッシュから取得された詳細リクエスト

ブラウザのアイドル時間を活用することで、プリフェッチは別のページに必要なリソースを早期にリクエストします。これにより、ブラウザがアセットをより早くキャッシュに保存し、必要に応じてキャッシュから提供できるようになるため、今後のナビゲーション リクエストが高速化されます。

webpack でのプリロードとプリフェッチ

コード分割で JavaScript ペイロードを削減するでは、動的インポートを使用してバンドルを複数のチャンクに分割する方法について説明しています。これは、フォームが送信されたときに Lodash からモジュールを動的にインポートするシンプルなアプリケーションで実証されています。

コード分割を実証する Magic Sorter アプリ

このアプリケーションの Glitch にはこちらからアクセスできます。

src/index.js, にある次のコードブロックは、ボタンがクリックされたときにメソッドを動的にインポートする役割を果たします。

form.addEventListener("submit", e => {
  e.preventDefault()
  import('lodash.sortby')
    .then(module => module.default)
    .then(sortInput())
    .catch(err => { alert(err) });
});

バンドルを分割すると、初期サイズが小さくなるため、ページの読み込み時間が短縮されます。webpack のバージョン 4.6.0 では、動的にインポートされるチャンクのプリロードまたはプリフェッチがサポートされています。このアプリを例にすると、ブラウザのアイドル時に lodash メソッドをプリフェッチできます。ユーザーがボタンを押したときに、リソースの取得に遅延は発生しません。

動的インポート内の特定の webpackPrefetch コメント パラメータを使用して、特定のチャンクをプリフェッチします。この特定のアプリケーションでは、次のようになります。

form.addEventListener("submit", e => {
  e.preventDefault()
  import(/* webpackPrefetch: true */ 'lodash.sortby')
    .then(module => module.default)
    .then(sortInput())
    .catch(err => { alert(err) });
});

アプリケーションが再読み込みされると、webpack はリソースのプリフェッチ タグをドキュメントの head に挿入します。これは、DevTools の [要素] パネルで確認できます。

プリフェッチ タグを含む [要素] パネル

[ネットワーク] パネルでリクエストを観察すると、このチャンクは他のすべてのリソースがリクエストされた後に低い優先度で取得されていることもわかります。

プリフェッチされたリクエストを含むネットワーク パネル

このユースケースではプリフェッチの方が適切ですが、webpack は動的にインポートされたチャンクのプリロードもサポートしています。

import(/* webpackPreload: true */ 'module')

まとめ

この Codelab を通して、特定のアセットをプリロードまたはプリフェッチすることでサイトのユーザー エクスペリエンスを向上させる方法をしっかりと理解できたはずです。これらの手法はすべてのリソースに使用すべきではなく、誤って使用するとパフォーマンスに悪影響を及ぼす可能性があることに注意してください。最適な結果は、選択的にプリロードまたはプリフェッチすることでのみ得られます。

まとめ

  • 遅れて検出されたが、現在のページにとって重要なリソースには preload を使用します。
  • 今後のナビゲーション ルートやユーザー アクションに必要なリソースには、プリフェッチを使用します。

現時点では、すべてのブラウザがプリロードとプリフェッチの両方をサポートしているわけではありません。つまり、アプリのすべてのユーザーがパフォーマンスの改善に気づくとは限りません。

プリロードとプリフェッチがウェブページに与える影響の特定の側面について詳しくは、以下の記事をご覧ください。