モジュールのプリフェッチ

Qwikは、モジュールを事前にプリフェッチするためのさまざまな戦略を提供しています。このページでは、Qwikのプリフェッチの**低レベル**機能について説明します。

モジュールをプリフェッチすることで、アプリケーションはユーザーが実際に必要とする前に、必要なコードのダウンロードをバックグラウンドで開始できます。理想的なソリューションは、ユーザーの操作から実行される可能性が非常に高い、関連するコードの最小量のみをプリフェッチし、*使用されない* JavaScriptを回避することです。

Qwikアプリケーションは、最小限のJavaScriptのダウンロードと実行に優れており、リソースの使用量とパフォーマンスを最適化します。個々のコンポーネントが使用されているかどうかわかることで、Qwikはどのバンドルをプリフェッチするかを効果的に決定できます。この的を絞ったアプローチにより、必要なコードのみがロードされます。

再開可能性とハイドレーションの重要な違いは、再開可能性により、Qwikアプリケーションはイベントリスナー、コンポーネントツリー、およびアプリケーション状態を復元するためだけにJavaScriptを実行することを回避できることです。コンポーネントのイベントリスナー、レンダー関数、および状態を根本的に分離することで、プリフェッチするコードの量は従来のアプローチに比べて大幅に少なくなります。

使用済みシンボルの収集

Qwikがアプリをレンダリングすると、レンダリング中に使用された「シンボル」を収集できます。シンボルには、コンポーネントのさまざまな部分が含まれており、オプティマイザーによってアプリケーションを分割するために抽出されます。個々のイベントリスナー、コンポーネントの状態、およびコンポーネントレンダラー自体は、抽出される可能性のあるさまざまなシンボルの例です。

たとえば、1つの「カートに追加」ボタンを除いて、ほとんど静的な製品ページを考えてみましょう。ボタンをクリックすると、ユーザーは製品がカートに追加されたことを示すフィードバックをすぐに受け取る必要があります。この例では、Qwikオプティマイザーは、ユーザーが操作できる唯一のシンボルが「カートに追加」ボタクリックイベントリスナーであることを理解できます。

「カートに追加」の例では、オプティマイザーは、クリックイベントリスナーとカートに追加ウィジェットのレンダラーのシンボルのみを収集します。関連のないアプリケーションの他の部分をダウンロード、ハイドレート、および再レンダリングする必要はありません。これは、Qwikが可能なインタラクションを判断し、イベントリスナーに必要なコードのみをプリフェッチする機能を示しています。対照的に、従来のアプローチでは、クリックイベントリスナーを追加するためだけに、フレームワークコードを含むアプリケーション全体またはルートをプリフェッチする必要があります。

プリフェッチ戦略

プリフェッチ戦略とは、QwikがバックグラウンドでプリフェッチするJavaScript(もしあれば)を決定するロジックです。デフォルトでは、Qwikはページに表示されているリスナーをプリフェッチします。プリフェッチ戦略を設定するには、多くの場合`src/entry.ssr.tsx`ソースファイルにある`renderToStream()`関数のオプション引数を使用します。最適なプリフェッチ戦略を提供することは、Qwikの継続的なコミットメントです。

export default function (opts: RenderToStreamOptions) {
  return renderToStream(<Root />, {
    manifest,
    prefetchStrategy: {
      // custom prefetching config
    },
    ...opts,
  });
}

実装

ブラウザは、プリフェッチ戦略を実装するための多くの方法を提供しています。Qwikは、それぞれに長所と短所がある、ある実装を別の実装よりも優先するように設定できます。この設定に応じて、生成されるHTMLコンテンツには、選択されたプリフェッチ実装が含まれます。

export default function (opts: RenderToStreamOptions) {
  return renderToStream(<Root />, {
    manifest,
    prefetchStrategy: {
      implementation: {
        // custom prefetching implementation
      },
    },
    ...opts,
  });
}
オプション説明
prefetchEventプリフェッチする必要があるURLを含む`detail`データを持つ`qprefetch`イベントをディスパッチします。イベントディスパッチスクリプトは、ドキュメントのHTMLにインライン化されます。デフォルトでは、`prefetchEvent`実装は`always`に設定されます。
linkInsert<link>要素をドキュメントに挿入します。 html-appendを使用すると、各<link>はhtml内に直接レンダリングされ、bodyの最後に追加されます。 js-appendオプションを使用すると、代わりにJavaScriptが挿入され、実行時に要素が作成され、bodyの最後に追加されます。
linkRelこのオプションは、<link>要素のrel属性を定義するために使用されます。 linkInsertオプションを使用する場合、デフォルトはprefetchです。その他のオプションには、preloadmodulepreloadがあります。
workerFetchInsert各モジュールに対してfetch()を呼び出してURLをプリフェッチし、ネットワークキャッシュにデータを格納することを目的としています。

ディスパッチされたプリフェッチイベント

投機的モジュールフェッチングは推奨されるキャッシュ戦略です。この戦略は、Qwikフレームワークによってディスパッチされるqprefetchイベントをリッスンします。イベントには、バックグラウンドスレッドがブラウザのキャッシュに事前にデータを入力するために使用するURLのリストが含まれています。

Qwikは、qprefetchイベントをディスパッチするprefetchEvent実装を使用するように設定する必要があります。デフォルトでは、prefetchEvent実装はalwaysに設定されます。次に、投機的モジュールフェッチングはこのイベントをリッスンし、サービスワーカーと通信してリクエスト/レスポンスオブジェクトのペアを永続化し、長期メモリにキャッシュされるようにします。

サービスワーカーを使用してブラウザからのfetchリクエストをインターセプトすることにより、このアプローチはキャッシュをきめ細かく制御し、同じリソースに対する重複リクエストを防ぐことができます。

以下は、イベントを手動でディスパッチする例です。これらのイベントはQwik自体からディスパッチされるため、開発者がこれらのイベントを手動でディスパッチする必要はありません。さらに、サービスワーカーは、これらのイベントのリスナーを自動的に追加します。

dispatchEvent(new CustomEvent("qprefetch", { detail: {
  bundles: [...]
}}));

rel属性を持つ<link>要素を使用することは、今日のフレームワークでは一般的なアプローチであり、QwikはlinkInsertおよびlinkRelオプションを設定することでこの方法を使用できます。リンクrelアプローチは効果的ですが、少なくとも執筆時点では、すべてのデバイスでサポートされていないという問題があります。さらに、開発中は、モバイルデバイスでのプリフェッチは簡単に見えないため、どこでも機能すると想定するのは誤解を招く可能性があります。

たとえば、Safariはmodulepreloadをサポートしていません。これは、モバイルデバイスがモジュールプリロードから最も恩恵を受ける可能性があるため、重要です。同様に、Firefoxは、httpsの場合、link rel prefetchをサポートしていません。

プリフェッチは、訪問者のエクスペリエンスの速度を向上させるように設計された機能です。ただし、効果は使用されるブラウザとCDN /サーバーの組み合わせによって異なる場合があり、最適なパフォーマンスを確保するための最適化されたセットアップの重要性を強調しています。

- Rel=prefetchと効果的なHTTP/2優先順位付けの重要性

さらに、同じリソースに対して複数のリクエストが発生する可能性があります。たとえば、module-a.jsをプリフェッチしたいとします。ダウンロードにかかる時間にかかわらず、ユーザーはアプリと対話します。その後、アプリは実際にmodule-a.jsをリクエストして実行することを決定します。この記事の執筆時点では、ブラウザは多くの場合、2回目のリクエストを発行して事態を悪化させます。

  • HTML仕様に含まれているとしても、エンドユーザーがアプリを正しくプリロードしているという意味ではありません。 Can I Use: modulepreload
  • Firefoxではサポートされていません。

Webワーカーフェッチ

workerFetchInsertは、QwikにWebワーカーを使用してJavaScriptファイルをfetch()するように指示し、ブラウザキャッシュにモジュールをプライミングすることを目的としています。Webワーカーを使用することにより、フェッチとキャッシュのロジックは別のスレッドに存在します。また、フェッチレスポンスにはimmutableまたはlong cache-controlヘッダーが含まれるため、ブラウザは2回目のネットワークリクエストを実行しません。

この設定の欠点は、フェッチされたレスポンスが破棄され、ファイルがキャッシュされるのはブラウザレベルでのみであることです。

プリフェッチに関するよくある質問

**質問**: *ユーザーイベントでの遅延読み込みは、ユーザーがコードのダウンロードを待機する必要があるため、遅いですか?*

はい、特に低速ネットワークでは、顕著な遅延が発生します。これが、コードのプリフェッチがQwikアプリケーションの重要な部分である理由です。

コードをプリフェッチすると、アプリケーションを実行するために必要なすべてのコードが、ページに移動した直後にフェッチされます。こうすることで、ユーザーがアクションを実行したときに、そのアクションのコードはネットワークではなくプリフェッチキャッシュから取得されます。その結果、コードの実行は瞬時に行われます。

**質問**: *コードのプリフェッチは、すべてのコードを積極的にダウンロードして実行する既存のフレームワークと同じ動作になりませんか?*

いいえ、いくつかの理由があります

  • 既存のフレームワークは、アプリケーションをインタラクティブにする前に、すべてのコードをダウンロードして実行する必要があります(ハイドレーション)。通常、コードのダウンロードにかかる時間は、コードの実行にかかる時間よりも短くなります。
  • Qwikコードプリフェッチはダウンロードのみを行い、コードは実行しません。したがって、Qwikが既存のフレームワークと同じ量のコードをプリフェッチしたとしても、結果として時間コストの大幅な節約になります。
  • Qwikは、現在のページに必要なコードのみをプリフェッチします。 Qwikは、静的なコンポーネントに関連付けられたコードのダウンロードを回避します。より多くのコードをプリフェッチする必要があるシナリオでは、Qwikは依然として既存のフレームワークが最良のケースと見なす量にのみ到達します。ほとんどの場合、Qwikは既存のフレームワークと比較して、ごく一部のコードをプリフェッチします。
  • コードのプリフェッチは、メインスレッド以外のスレッドで行うことができます。多くのブラウザは、メインスレッドからコードのASTを事前に解析することさえできます。
  • プリフェッチが完了する前にユーザーインタラクションが発生した場合、ブラウザは残りのプリフェッチチャンクの前にインタラクションチャンクを自動的に優先します。
  • Qwikはアプリケーションを多数の小さなチャンクに分割でき、これらのチャンクはユーザーがそれらと対話する確率の順にダウンロードできます。既存のフレームワークは、アプリケーションを小さなチャンクに分割するのが難しく、ハイドレーションにはアプリケーションへの単一の「メイン」エントリポイントが必要なため、チャンクのダウンロード順序の優先順位付けは容易ではありません。

**質問**: *どのコードをプリフェッチするかを知る責任は誰にありますか?*

Qwikは、SSRレンダリングの一部としてプリフェッチ命令を自動的に生成できます。アプリケーションを実行することにより、Qwikはどのコンポーネントが表示されているか、ユーザーがどのイベントをトリガーできるか、どのコードをダウンロードする必要があるかを実行時に認識します。その結果、プリフェッチはこのページに最適なファイルセットになります。開発者がrenderToStream()にプリフェッチ戦略を追加する以外に、操作は必要ありません。

貢献者

このドキュメントの改善に貢献してくれたすべての貢献者に感謝します!

  • adamdbradley
  • RATIU5
  • manucorporat
  • literalpie
  • saikatdas0790
  • the-r3aper7
  • mhevery
  • mrhoodz
  • thejackshelton
  • maiieul
  • jemsco