Skip to content

Beyond Fast

ViteConf 2023

Watch the replay!

依存関係の事前バンドル

初めて、vite を実行すると、Vite は、あなたのサイトをローカルで読み込む前に、プロジェクトの依存関係を事前バンドルします。これはデフォルトで自動的に、かつ透過的に行われます。

その理由は?

これは、Vite が「依存関係の事前バンドル」を実行しています。

このプロセスには 2 つの目的があります:

  1. CommonJS と UMD の互換性: 開発中の Vite のコードは ECMAScript モジュールとして提供しています。そのため、Vite は、CommonJS または、UMD を ESM に変換する必要があります。CommonJS の依存関係を変換する場合、Vite はインポート文をスマート分析を実行してエクスポートが動的に割り当てられていても、CommonJS モジュールは期待通りに動作します。(例 React):
js
// works as expected
   import React, { useState } from 'react'
// works as expected
   import React, { useState } from 'react'
  1. パフォーマンス: Vite は、多くの内部モジュールを持つ ESM の依存関係を単一のモジュールに変換して、その後のページロードのパフォーマンスを向上させます。いくつかのパッケージでは、ECMAScript モジュールのビルドを、相互にインポートする別々のファイルとして出力します。

一例として lodash-es には、600 以上の内部モジュールがあります。import { debounce } from 'lodash-es' をすると、ブラウザは 600 以上の HTTP リクエストを同時に処理します! サーバ側では問題なく処理していても、大量のリクエストによりブラウザ側でネットワークの混雑が発生し、ページの読み込みが著しく遅くなってしまいます。

事前に lodash-es を単一のモジュールにバンドルすることにより HTTP リクエストは 1 つだけで済むようになりました。

注意

依存関係の事前バンドルは開発モードでのみ適用され、依存関係を ESM に変換するために esbuild を使用します。本番ビルドでは、代わりに @rollup/plugin-commonjs が使用されます。

依存関係の自動検出

既存のキャッシュが見つからない場合、Vite はソースコードをクロールし、依存関係のインポート(すなわち、node_modules から解決されることを期待されている "bare imports")を自動的に検出します。そして、検出されたインポートを事前バンドルのエントリポイントとして使用します。事前バンドルは esbuild で実行されるので、例のごとく非常に高速です。

サーバを起動したあと、キャッシュにない新しい依存関係のインポートに遭遇した場合は、Vite は依存関係の事前バンドルを再実行し、必要に応じてページをリロードします。

モノレポとリンクされた依存関係

モノレポの設定では、依存関係は同じリポジトリからのリンクされたパッケージの可能性があります。Vite は node_modules から解決されない依存関係を自動的に検出し、リンクされた依存関係をソースコードとして扱います。リンクされた依存関係をバンドルしようとはせず、代わりにリンクされた依存関係のリストを分析します。

ただしこの場合、リンクされた依存関係が ESM としてエクスポートされている必要があります。そうでない場合は、optimizeDeps.includebuild.commonjsOptions.include に依存関係を追加して、設定することができます。

js
export default defineConfig({
  optimizeDeps: {
    include: ['linked-dep'],
  },
  build: {
    commonjsOptions: {
      include: [/linked-dep/, /node_modules/],
    },
  },
})
export default defineConfig({
  optimizeDeps: {
    include: ['linked-dep'],
  },
  build: {
    commonjsOptions: {
      include: [/linked-dep/, /node_modules/],
    },
  },
})

リンクされた依存関係を変更する場合は、--force コマンドラインオプションを指定して開発サーバーを再起動すると、変更が有効になります。

挙動のカスタマイズ

デフォルトのヒューリスティックな方法による依存関係の発見によって、必ずしも望ましい結果が得られるとは限りません。リストから依存関係を明示的に含めたり除外したりする場合は、optimizeDeps 設定オプションを使用してください。

optimizeDeps.include または optimizeDeps.exclude の一般的な使用例は、ソースコードで直接検出できないインポートがある場合です。たとえば、インポートはプラグイン変換の結果として作成される可能性があります。これは、Vite が最初のスキャンでインポートを検出できないことを意味します。つまり、ファイルがブラウザによって要求されて変換された後にのみ、インポートを検出できます。 これにより、サーバの起動後すぐにサーバが再バンドルされます。

これには、includeexclude の両方が使用できます。依存関係が大きい(多くの内部モジュールがある)場合や、CommonJS の場合には、それを含める必要があります。依存関係が小さく、すでに有効な ESM の場合には、それを除外し、ブラウザに直接読み込ませることができます。

esbuild も optimizeDeps.esbuildOptions オプションでさらにカスタマイズできます。例えば、esbuild のプラグインを追加して、依存関係にある特殊なファイルを扱えるようにするか、build target を変更します。

キャッシュ

File System キャッシュ

Vite は、node_modules/.vite に、事前バンドル済みの依存関係をキャッシュします。いくつかのソースに基づいて、事前バンドルを再実行する必要があるかどうか決定します:

  • パッケージマネージャのロックファイルの内容、例: package-lock.jsonyarn.lockpnpm-lock.yamlbun.lockb など。
  • もし存在すれば、vite.config.js の関連するフィールド。
  • パッチフォルダの変更時間。
  • NODE_ENV の値。

上記のいずれかが変更された場合のみ、事前バンドルを再実行する必要があります。

何らかの理由で Vite に再バンドルを強制したい場合は、開発サーバを --force コマンドラインオプションで起動するか、手動で node_modules/.vite のキャッシュディレクトリを削除します。

ブラウザ キャッシュ

解決された依存関係のリクエストは、開発中のページの再読み込みのパフォーマンスを向上させるために、HTTP ヘッダ max-age = 31536000、immutable で積極的にキャッシュされます。一度キャッシュされると、これらのリクエストは開発サーバに再び到達することはありません。異なるバージョンがインストールされた(パッケージマネージャのロックファイルに反映された)場合は、付与されているバージョンクエリによって自動的に無効になります。ローカルでの編集で依存関係をデバッグしたい場合は、以下のように行えます:

  1. ブラウザの devtools のネットワークタブからキャッシュを一時的に無効にします。
  2. Vite 開発サーバを --force フラグで再起動して、依存関係を再バンドルします。
  3. ページをリロードします。

Released under the MIT License. (fa1cfd34)