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

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

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

測定

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

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

Glitch の公開版で Lighthouse パフォーマンス監査([Lighthouse] > [Options] > [Performance])を実行します(Lighthouse でパフォーマンス改善の機会を見つけるも参照)。

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

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

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

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

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

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

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

main.css ファイルは、ページの読み込み直後にすぐに必要な重要なアセットです。このリソースのような重要なファイルがアプリの後半でフェッチされる場合は、リンク プリロード タグを使用して、ドキュメントのヘッドに Link 要素を追加し、ブラウザに早めにダウンロードするよう指示します。

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

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

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

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

リソースがプリロードされているネットワーク パネル

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

プリロードを適切に使用しないと、使用されていないリソースを不必要にリクエストしてパフォーマンスに悪影響を及ぼす可能性があります。このアプリケーションでは、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 の記事をご覧ください。

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

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

このウェブサイトで画像をクリックすると、別の 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 によるプリロードとプリフェッチ

Code Splitting による 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 はリソースのプリフェッチ タグをドキュメントのヘッダーに挿入します。これは、DevTools の [要素] パネルで確認できます。

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

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

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

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

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

まとめ

この Codelab では、特定のアセットをプリロードまたはプリフェッチすることでサイトのユーザー エクスペリエンスを改善する方法について理解を深めます。ただし、これらの手法はすべてのリソースに使用できるわけではなく、誤って使用するとパフォーマンスに悪影響を与える可能性があります。最適な結果を得るには、プリロードまたはプリフェッチを必要に応じて行う必要があります。

まとめ

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

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

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