Vite を使う理由
問題点
ES モジュールがブラウザーで利用できるようになるまで、開発者はモジュール化された JavaScript を生成するネイティブの仕組みを持っていませんでした。これは、私たちが「バンドル」のコンセプトに慣れ親しんでいる理由でもあります: すなわち、ブラウザーで実行可能なようにソースモジュールをクロール、処理し、連結するツールを使用しています。
時を経て webpack や Rollup、Parcel のようなツールが登場し、フロントエンド開発者の開発体験は大きく向上されました。
しかしながら、大規模なアプリケーションが作られるようになってくると、取り扱う JavaScript の量は劇的に増加しました。大規模プロジェクトでは、数千ものモジュールが含まれることも珍しくありません。JavaScript ベースのツールを使用していては、いずれパフォーマンスのボトルネックにぶつかります: 開発サーバーを起動するのにやたらと長く待つこともあります(数分かかることさえ!)。また、Hot Module Replacement(HMR)を利用していても、ファイル編集がブラウザーに反映されるまで数秒かかることもあります。フィードバックの遅さが継続することは、開発者の生産性や幸福度に大きな影響を与える可能性があります。
Vite では新しいエコシステムの進歩を活用し、これらの問題を解決することに取り組んでいます: ブラウザーのネイティブ ES モジュールや、ネイティブにコンパイルされる言語で書かれた先進的な JavaScript ツールの利用です。
遅いサーバー起動
開発サーバーがコールドスタートするとき、バンドラーベースのビルドセットアップは、アプリケーション全体を提供する前に、アプリケーション全体を隅々までクロールしてビルドする必要があります。
Vite はまず最初にアプリケーションのモジュールを 2 つのカテゴリーに分割することで、開発サーバーの起動時間を改善します: 依存関係とソースコードです。
依存関係の大部分は開発中あまり変更されないプレーンな JavaScript です。巨大な依存関係の中には、処理コストが極めて高いものがあります(例: 100 ものモジュールを持つコンポーネントライブラリー)。依存関係は、様々なモジュール形式で出力されることがあります(例: ESM または CommonJS)。
Vite は、esbuild を使用して依存関係の事前バンドルを行います。esbuild は Go 言語によって開発されており、依存関係の事前バンドルは、JavaScript ベースよりも 10 倍から 100 倍高速です。
ソースコードには変換を必要とするプレーンな JavaScript ではないものが含まれることがよくあり、頻繁に編集されます(例: JSX、CSS や Vue/Svelte コンポーネント)。また、全てのソースコードを同時に読み込む必要はありません(例: ルーティングによるコード分割)。
Vite は、ネイティブ ESM を行使してソースコードを提供します。ブラウザーは、実質的にバンドラーの仕事の一部を引き受けます: Vite はブラウザーのリクエストに応じて、ソースコードを変換し提供するのみになります。条件で囲われている動的インポートのコードは、現在の画面で使われる場合のみ処理されます。
遅い更新速度
バンドラーベースのビルドセットアップでファイルが編集されたとき、全てのバンドルを再構築することが非効率なことは明白です: 更新スピードはアプリケーションのサイズに応じて線形的に低下してしまうからです。
バンドラーの中には開発サーバーがメモリー上でバンドルを実行し、ファイルが変更されたときにモジュールグラフの一部のみ無効化して再処理を行うものも存在しますが、それでもバンドル全体を再構築して、ウェブページをリロードしなければなりません。バンドルの再構築にはコストがかかりますし、ページがリロードされるとアプリケーションの現在の状態は消えてしまいます。そのため、幾つかのバンドラーは HMR(ホットモジュールリプレースメント)をサポートしています: これにより、ページの変更に関係のない部分には影響を与えることなく、モジュールを「ホットリプレース」することができます。これは開発者体験を大きく改善します。- しかしながら、実際には HMR でもアプリケーションが大きくなるにつれ更新速度が著しく悪化することが分かってきました。
Vite では、HMR をネイティブ ESM 上で行います。ファイルが編集されたとき、Vite は編集されたモジュールと最も近い HMR バウンダリ間のつながりを正確に無効化することで(大抵はモジュール本体だけです)、HMR による更新はアプリケーションのサイズに関係なく一貫して高速で実行されます。
また、Vite は HTTP ヘッダーを活用して、フルページのリロードも高速化します(ここでも、ブラウザーにはもっと働いてもらいます): ソースコードモジュールのリクエストでは 304 Not Modified
を利用して条件が作成されます。そして、依存モジュールのリクエストでは、一度キャッシュされたものが再びサーバーにヒットしないよう、Cache-Control: max-age=31536000,immutable
を利用して積極的にキャッシュされます。
超高速な Vite を一度体験してしまうと、バンドルでの開発にまた耐えられるかはとても疑わしいです。
プロダクションではバンドルする理由
ネイティブ ESM が広くサポートされるようになっても、バンドルされていない ESM をプロダクション用にリリースすることは非効率です(HTTP/2 を利用していても)。これは、ネットワークのラウンドトリップの増加がネストされたインポートによって引き起こされるためです。プロダクションでは最適化されたローディングパフォーマンスを得るために、ツリーシェイキングや遅延読み込み、(キャッシュ改善のための)共通コード分割などの技術を用いつつバンドルを行うことは、より良いことです。
開発サーバーとプロダクションビルド間で、最適化された出力と一貫した動作を確保することは容易なことではありません。そのため、Vite にはあらかじめ調整されたビルドコマンドが用意されており、これには従来の常識を破る多くのパフォーマンス最適化が施されています。
なぜ esbuild でバンドルしないのか?
Vite は 開発環境で一部の依存関係を事前バンドルするために esbuild を活用していますが、本番ビルドのためのバンドラーとしては esbuild を利用しません。
Vite の現在のプラグイン API は、esbuild
をバンドラーとして使用することと互換性がありません。esbuild
の方が速いにもかかわらず、Vite は Rollup の柔軟なプラグイン API とインフラストラクチャーを採用し、エコシステムでの成功に大きく貢献しました。当面は、Rollup の方がパフォーマンスと柔軟性のトレードオフに優れていると考えています。
Rollup はパフォーマンスの向上にも取り組んでおり、v4 でパーサーを SWC に切り替えました。 そして、Rolldown と呼ばれる Rollup の Rust ポートを構築する取り組みが進行中です。 Rolldown の準備が完了すると、Vite の Rollup と esbuild の両方が置き換えられ、ビルドのパフォーマンスが大幅に向上し、開発とビルドの間の不一致が解消されます。 詳細については、Evan You の ViteConf 2023 基調講演 をご覧ください。
Vite とその他 X はどう違いますか?
Vite がその他の類似ツールとどう違うのか、より詳細な違いは、比較 セクションで確認してください。