公開日: 2024 年 11 月 23 日
モジュールベースの開発は、キャッシュ保存の点で大きなメリットがあり、ユーザーに送信する必要があるバイト数を削減できます。コードの粒度を細かくすることで、アプリケーション内の重要なコードに優先順位を付けることができるため、読み込みのストーリーにも役立ちます。
ただし、モジュールの依存関係により読み込みの問題が発生します。ブラウザは、モジュールが読み込まれるのを待ってから、その依存関係を検出する必要があります。この問題を回避する 1 つの方法は、依存関係をプリロードして、ブラウザがすべてのファイルを事前に認識し、接続をビジー状態に保つようにすることです。
<link rel="preload">
<link rel="preload">
は、ブラウザがリソースを必要とする前に、事前にリソースを宣言的にリクエストする方法です。
<head>
<link rel="preload" as="style" href="critical-styles.css">
<link rel="preload" as="font" crossorigin type="font/woff2" href="myfont.woff2">
</head>
これは、CSS ファイル内に隠されているフォントなどのリソースに特に適しています。このような状況では、ブラウザは複数のラウンドトリップを待ってから、大きなフォント ファイルを取得する必要があることを認識する必要があります。その間にダウンロードを開始して、接続帯域幅を最大限に活用できたはずです。
<link rel="preload">
とその HTTP ヘッダー同等物は、現在のナビゲーションの一部として必要となる重要なファイルについて、ブラウザにすぐに通知できるシンプルな宣言型の方法です。ブラウザはプリロードを検出すると、リソースの優先度の高いダウンロードを開始します。これにより、実際に必要になるまでに、リソースがすでにフェッチされているか、一部がフェッチされている状態になります。ただし、モジュールでは機能しません。
モジュールで <link rel="preload">
が機能しないのはなぜですか?
ここで状況が複雑になります。リソースの認証情報モードはいくつかあり、キャッシュヒットを取得するには、それらが一致している必要があります。一致していないと、リソースが 2 回フェッチされます。言うまでもなく、ダブル フェッチは望ましくありません。ユーザーの帯域幅を浪費し、理由もなく待ち時間を長くするためです。
<script>
タグと <link>
タグの場合は、crossorigin
属性で認証情報のモードを設定できます。ただし、crossorigin
属性のない <script type="module">
は、omit
の認証情報モードを示していますが、これは <link rel="preload">
には存在しません。つまり、<script>
と <link>
の両方の crossorigin
属性を他の値に変更する必要があります。プリロードしようとしているものが他のモジュールの依存関係である場合、簡単に変更できない可能性があります。
さらに、ファイルの取得は、コードを実際に実行する最初のステップにすぎません。まず、ブラウザがそれを解析してコンパイルする必要があります。理想的には、モジュールが必要になったときにコードを実行できるように、事前にこの処理を行う必要があります。ただし、V8(Chrome の JavaScript エンジン)は、他の JavaScript とは異なる方法でモジュールを解析してコンパイルします。<link rel="preload">
には、読み込まれるファイルがモジュールであることを示す方法がないため、ブラウザはファイルを読み込んでキャッシュに保存するだけです。スクリプトが <script type="module">
タグを使用して読み込まれると(または別のモジュールによって読み込まれると)、ブラウザはコードを解析して JavaScript モジュールとしてコンパイルします。
<link rel="modulepreload">
はモジュールの <link rel="preload">
にすぎないのですか?
簡単に言うと、はい。モジュールのプリロードに特定の link
タイプを使用すると、使用している認証情報のモードを気にせずにシンプルな HTML を記述できます。デフォルト設定で問題なく動作します。
<head>
<link rel="modulepreload" href="super-critical-stuff.mjs">
</head>
[...]
<script type="module" src="super-critical-stuff.mjs">
ブラウザはプリロード対象がモジュールであることを認識できるため、実行を試みるまで待機するのではなく、フェッチが完了するとすぐにモジュールを解析してコンパイルできます。
モジュールの依存関係はどうなりますか?
お問い合わせいただきありがとうございます。この記事では、再帰について説明していません。
実際には、<link rel="modulepreload">
仕様では、リクエストされたモジュールだけでなく、その依存関係ツリー全体を読み込むこともできます。ブラウザはこれを実行する必要はありませんが、実行できます。
アプリを実行するには完全な依存関係ツリーが必要になるため、モジュールとその依存関係ツリーをプリロードするための最適なクロスブラウザ ソリューションは何ですか?
依存関係を再帰的にプリロードするブラウザは、モジュールの堅牢な重複除去を行う必要があります。そのため、一般的なベスト プラクティスとしては、モジュールとその依存関係のフラットリストを宣言し、同じモジュールを 2 回取得しないようにブラウザに信頼させることが推奨されます。
<head>
<!-- dog.js imports dog-head.js, which in turn imports
dog-head-mouth.js, which imports dog-head-mouth-tongue.js. -->
<link rel="modulepreload" href="dog-head-mouth-tongue.mjs">
<link rel="modulepreload" href="dog-head-mouth.mjs">
<link rel="modulepreload" href="dog-head.mjs">
<link rel="modulepreload" href="dog.mjs">
</head>
モジュールをプリロードするとパフォーマンスが向上しますか?
プリロードは、ブラウザにフェッチする必要があるものを通知することで、長いラウンドトリップ中に何もできない状態にならないようにし、帯域幅の使用を最大化できます。モジュールをテストしていて、依存関係ツリーが深いためにパフォーマンスの問題が発生している場合は、プリロードのフラットなリストを作成すると効果的です。
ただし、モジュールのパフォーマンスはまだ開発中であるため、デベロッパー ツールでアプリの動作を詳しく確認し、その間にアプリを複数のチャンクにバンドルすることを検討してください。ただし、Chrome ではモジュールに関する作業がまだ多数進行中であるため、バンドル作成ツールは今後、休息に入る予定です。