コード分割 JavaScript

大量の JavaScript リソースを読み込むと、ページの読み込み速度に大きく影響します。分割 JavaScript を小さなチャンクに分割して、必要な分だけ ページが起動時に正常に機能することで、ページの読み込みを大幅に改善できる 応答性が向上し、ページの次のコンテンツへの ペイント(INP)

ページで大きな JavaScript ファイルをダウンロード、解析、コンパイルすると、 反応がない状態が一定時間続くことがありますページ要素は これはページの最初の HTML の一部であり、CSS によってスタイルが設定されます。ただし、JavaScript では これらのインタラクティブ要素や、スクリプトによって読み込まれる他のスクリプトを強化するために必要な 機能のために JavaScript を解析して実行することもあれば、「 ユーザーはやり取りが非常に重要であったかのように感じることがあります。 完全に破損する可能性もあります

これは多くの場合、JavaScript の解析時にメインスレッドがブロックされる場合に発生します。 メインスレッドでコンパイルしますこのプロセスに時間がかかりすぎる場合は ユーザー入力に対してページ要素が迅速に反応しないことがあります。これを防ぐ一つの解決策は ページの機能に必要な JavaScript だけを読み込むことであり、 コードと呼ばれる手法を使って、他の JavaScript が後で読み込まれるように遅延する 分割しますこのモジュールでは、これら 2 つの手法のうち後者に焦点を当てます。

コード分割により、起動時の JavaScript の解析と実行を削減

Lighthouse では、JavaScript の実行に 2 時間以上かかる場合に警告が表示される 完了し、3.5 秒を超えると失敗します。過度の JavaScript ページの任意の場所で解析と実行が問題となり得る インタラクションの入力遅延が増加する可能性があるため、 ユーザーがページを操作した時間が JavaScript の処理と実行を担当するメインスレッドのタスクが できます。

さらに、JavaScript の過剰な実行と解析は特に 最初のページの読み込み時に問題が発生します。 ユーザーがページを操作する可能性が非常に高いという ライフサイクルを確認できます実際、 負荷の応答性指標である Total Blocking Time(TBT)には高い相関関係がある INP との組み合わせで、ユーザーが操作を試みる傾向がある メッセージを表示することもできます。

各 JavaScript ファイルの実行にかかった時間を報告する Lighthouse 監査 ページ リクエストからページ リクエストが送信されると、 スクリプトはコード分割の候補になる場合があります。さらに Chrome DevTools のカバレッジ ツールを使用して、 ページの読み込み時にページの JavaScript が使用されなくなると、

コード分割は、ページの初期 JavaScript を削減できる便利な手法です。 記述できます。これにより、JavaScript バンドルを 2 つの部分に分割できます。

  • ページの読み込み時に必要な JavaScript のため、他のどのページにも読み込めない あります。
  • 後で読み込むことが可能な残りの JavaScript(ほとんどの場合、 ユーザーが画面上の特定のインタラクティブ要素を操作 表示されます。

コード分割を行うには、動的 import() 構文を使用します。この 特定の JavaScript リソースをリクエストする <script> 要素とは異なり、 - 後で JavaScript リソースを要求します。 ページのライフサイクル全体を管理できます。

<ph type="x-smartling-placeholder">
document.querySelectorAll('#myForm input').addEventListener('blur', async () => {
  // Get the form validation named export from the module through destructuring:
  const { validateForm } = await import('/validate-form.mjs');

  // Validate the form:
  validateForm();
}, { once: true });

上記の JavaScript スニペットでは、validate-form.mjs モジュールは ユーザーがフォームのいずれかにぼかしを施した場合にのみダウンロード、解析、実行 <input> フィールド。この場合、変更を行う JavaScript リソースが、 フォームの検証ロジックの動作がページに関連するのは、 使用される可能性が最も高い部分です。

JavaScript バンドラ(webpackParcelRollupesbuild など)は、 JavaScript バンドルを小さなチャンクに分割するよう構成されています。 ソースコード内で動的な import() 呼び出しが発生する。これらのツールのほとんどは 特に esbuild ではこの機能を有効にする必要があり、 役立ちます

<ph type="x-smartling-placeholder">

コードの分割に関する注意事項

コード分割はメインスレッドの競合を減らす効果的な方法ですが、 最初のページ読み込み時に、次の点に注意してください。 JavaScript のソースコードを監査して、コード分割の機会を探してください。

可能であればバンドラを使用する

API 呼び出しでは、デベロッパーが JavaScript モジュールを使用することが一般的です。 開発プロセスに集中できます。開発者エクスペリエンスが大幅に改善され コードの可読性と保守性が向上します。ただし、 JavaScript を配布する際に生じる可能性のある、最適ではないパフォーマンス特性 本番環境にデプロイできます。

最も重要なことは、バンドラを使用してソースを処理して最適化することです。 (コード分割を行うモジュールを含む)。バンドラは JavaScript ソースコードに最適化を適用するだけでなく、 パフォーマンスに関する考慮事項とのバランスを取るのに非常に効果的です 圧縮率に比例します圧縮効率はバンドルサイズに比例して向上し、 バンドラは、バンドルのサイズが大きく、 時間のかかるタスクを自動化できます。

Bundler は、バンドルされていない多数のモジュールを配布する問題も回避できる 接続します。JavaScript モジュールを使用するアーキテクチャは、 複雑なモジュールツリーですモジュール ツリーを切り離すと、各モジュールは 追加の HTTP リクエストが発生し、ウェブアプリのインタラクティビティが モジュールをバンドルしません。kubectl の 大きなモジュール ツリーを早期に読み込む <link rel="modulepreload"> リソースヒント 読み込みパフォーマンスの観点から、JavaScript バンドルが推奨されます。 見ていきます

ストリーミング コンパイルを誤って無効にしない

Chromium の V8 JavaScript エンジンには、すぐに使えるさまざまな最適化機能が用意されています を使用して、本番環境の JavaScript コードができるだけ効率的に読み込まれるようにします。 このような最適化の一つが「ストリーミング コンパイル」と呼ばれています。 ブラウザにストリーミングされた HTML の増分解析 ネットワークから到着する JavaScript。

アプリケーションに対してストリーミング コンパイルを確実に実行する方法は 2 つあります。 Chromium でウェブ アプリケーションを使用するには:

  • 本番環境のコードを変換して、JavaScript モジュールの使用を避ける。バンドラ コンパイル ターゲットに基づいて JavaScript ソースコードを変換する 多くの場合、ターゲットは特定の環境に固有のものです。V8 はストリーミングを適用します モジュールを使用しない JavaScript コードにコンパイルできます。また、 Bundler を構成して、JavaScript モジュール コードを 使っていません。
  • JavaScript モジュールを本番環境に配布する場合は、.mjs 拡張機能です。本番環境の JavaScript でモジュールを使用しているかどうかにかかわらず、 モジュールを使用する JavaScript と JavaScript 用の特別なコンテンツ タイプがない サポートしています。V8 が懸念される場合、ストリーミングは実質的にオプトアウトします。 .js を使用して JavaScript モジュールを本番環境で配布する場合のコンパイル あります。JavaScript モジュールに .mjs 拡張機能を使用すると、V8 で モジュール ベースの JavaScript コードのストリーミング コンパイルが、 あります。

以上のことから、コード分割の使用を思いとどまることがあってはなりません。コード 分割は、ユーザーへの最初の JavaScript ペイロードを削減する効果的な方法です。 バンドラを使用して V8 のストリーミングを 動作に応じて、本番環境の JavaScript コードが できる限り速くユーザーに届けます

動的インポートのデモ

Webpack

webpack には SplitChunksPlugin というプラグインが付属しています。これにより、 Bundler による JavaScript ファイルの分割方法を構成します。Webpack は 動的 import() および静的 import ステートメント。Pod の動作を SplitChunksPlugin は、chunks オプションを 構成:

  • chunks: async はデフォルト値で、動的な import() 呼び出しを指します。
  • chunks: initial は静的な import 呼び出しを指します。
  • chunks: all は、動的 import() と静的インポートの両方に対応しているため、 asyncinitial のインポート間でチャンクを共有します。

デフォルトでは、webpack が動的な import() ステートメントを見つけるたびに実行されます。 そのモジュール用に別のチャンクを作成します。

/* main.js */

// An application-specific chunk required during the initial page load:
import myFunction from './my-function.js';

myFunction('Hello world!');

// If a specific condition is met, a separate chunk is downloaded on demand,
// rather than being bundled with the initial chunk:
if (condition) {
  // Assumes top-level await is available. More info:
  // https://v8.dev/features/top-level-await
  await import('/form-validation.js');
}

上記のコード スニペットのデフォルトの webpack 構成では、2 つの結果が生成されます。 分割します。

  • main.js チャンク(webpack が initial チャンクに分類される)は、 main.js モジュールと ./my-function.js モジュールが含まれています。
  • async チャンク。これには form-validation.js( (構成されている場合)、リソース名のファイル ハッシュ)。このチャンクはダウンロードされます conditiontruthy かどうかも確認します。

この構成を使用すると、form-validation.js チャンクの読み込みを次の時点まで延期できます。 判断できますこれにより、スクリプトが削減され、読み込みの応答性が向上します。 最初のページ読み込み時に評価が行われます。スクリプトのダウンロードと評価 form-validation.js チャンクは、指定された条件が満たされたときに発生します。 動的にインポートされたモジュールがダウンロードされます。たとえば 特定のブラウザについてのみポリフィルがダウンロードされる条件 前の例と同じく、インポートされたモジュールはユーザーの操作に必要です。

一方、SplitChunksPlugin 構成を変更して、 chunks: initial は、コードが最初のチャンクでのみ分割されるようにします。これらは チャンク(静的にインポートされたものや、webpack の entry にリストされるチャンクなど) プロパティをご覧ください。上記の例を見ると、生成されるチャンクは次のようになります。 1 つのスクリプト ファイル内の form-validation.js main.js の組み合わせ 最初のページ読み込みのパフォーマンスが低下する可能性があります

SplitChunksPlugin のオプションを設定して、大きいサイズを区切ることもできます。 たとえば、maxSize オプションを使用するなどして、複数の小さなスクリプトに分割します。 上限を超えた場合にチャンクを別々のファイルに分割するよう webpack に指示する maxSize で指定された値。大きなスクリプト ファイルを小さなファイルに分割すると、 読み込みの応答性を向上させる。たとえば、スクリプトの評価で CPU 使用率が高い場合があるためです。 作業が小さなタスクに分割されるため、メインのタスクがブロックされにくい 表示させない方がよいでしょう。

さらに、サイズの大きな JavaScript ファイルを生成すると、 キャッシュ無効化の問題が発生しやすくなりますたとえば、非常に フレームワークとファーストパーティ アプリケーション コードの両方を含む大規模なスクリプトを使用すると、 フレームワークのみが更新され、それ以外は更新されていない場合は、バンドルを無効化できます 作成します。

一方、スクリプト ファイルが小さいほど、リターンされる可能性が高くなります。 ユーザーがキャッシュからリソースを取得するため、ページの読み込みが速くなります 再訪問する傾向にありますただし、サイズが小さいファイルの方が、大きいファイルよりも圧縮によるメリットが少なくなります。 ネットワーク ラウンド トリップ時間が長くなる可能性があります。 ブラウザ キャッシュに保存できます。キャッシュ保存のバランスを取るように注意する必要がある 効率性、圧縮効率、スクリプト評価時間です

<ph type="x-smartling-placeholder">

webpack のデモ

<ph type="x-smartling-placeholder">

webpack SplitChunksPlugin デモ

理解度テスト

コード実行時に使用される import ステートメントのタイプはどれですか。 ありますか?

静的 import
動的なimport()

先頭になければならない import ステートメントのタイプはどれですか。 他の場所に配置されていないか?

静的 import
動的なimport()

webpack で SplitChunksPlugin を使用する場合 async チャンクと initial チャンク?

async チャンクは、静的な import を使用して読み込まれます および initial チャンクは、 import()
async チャンクは、動的な import() を使用して読み込まれます および initial チャンクは、 import

次のステップ: 画像と <iframe> 要素の遅延読み込み

かなり高価な種類のリソースになりがちですが、JavaScript は 読み込みを遅らせることができる唯一のリソースタイプです。画像と <iframe> 要素 それ自体でコストがかかるリソースですJavaScript と同様に、 遅延読み込みによって、画像と <iframe> 要素の読み込みを遅らせることができます。 これについては、このコースの次のモジュールで説明します。