Viteとは
問題点
ブラウザでESモジュールが利用可能になる前は、開発者はJavaScriptをモジュール化された方法で作成するためのネイティブな仕組みを持っていませんでした。これが、私たちが「バンドル」という概念に慣れ親しんでいる理由です。つまり、ソースモジュールをクロール、処理、連結して、ブラウザで実行できるファイルにするツールを使用することです。
時が経つにつれて、webpack、Rollup、Parcelのようなツールが登場し、フロントエンド開発者の開発体験を大幅に向上させました。
しかし、より野心的なアプリケーションを構築するにつれて、扱うJavaScriptの量も劇的に増加しています。大規模なプロジェクトでは、何千ものモジュールが含まれることは珍しくありません。JavaScriptベースのツールではパフォーマンスのボトルネックにぶつかり始めており、開発サーバーを起動するのに不合理に長い時間(時には数分!)かかり、ホットモジュールリプレイスメント(HMR)を使用しても、ファイルの編集がブラウザに反映されるまでに数秒かかることがあります。この遅いフィードバックループは、開発者の生産性と幸福感に大きく影響します。
Viteは、エコシステムにおける新しい進歩、すなわちブラウザでのネイティブESモジュールの利用可能性と、コンパイルしてネイティブ言語に変換されるJavaScriptツールの台頭を活用することで、これらの問題に対処することを目指しています。
サーバー起動の遅延
開発サーバーをコールドスタートする際、バンドルベースのビルド設定では、提供する前にアプリケーション全体を熱心にクロールしてビルドする必要があります。
Viteは、まずアプリケーション内のモジュールを**依存関係**と**ソースコード**の2つのカテゴリに分割することで、開発サーバーの起動時間を改善します。
**依存関係**は、開発中に頻繁に変わることのない、ほとんどがプレーンなJavaScriptです。一部の大きな依存関係(数百のモジュールを持つコンポーネントライブラリなど)も処理にかなりコストがかかります。依存関係は、さまざまなモジュール形式(ESMやCommonJSなど)で出荷されることもあります。
Viteはesbuildを使って依存関係を事前バンドルします。esbuildはGoで書かれており、JavaScriptベースのバンドラーよりも10~100倍高速に依存関係を事前バンドルします。
**ソースコード**は、変換が必要なプレーンではないJavaScript(JSX、CSS、Vue/Svelteコンポーネントなど)を頻繁に含み、非常に頻繁に編集されます。また、すべてのソースコードを同時にロードする必要はありません(たとえば、ルートベースのコード分割の場合)。
ViteはネイティブESMを介してソースコードを提供します。これは基本的に、バンドラーの仕事の一部をブラウザに引き継がせることを意味します。Viteは、ブラウザが要求する際にオンデマンドでソースコードを変換して提供するだけで済みます。条件付き動的インポートの背後にあるコードは、現在の画面で実際に使用された場合にのみ処理されます。
遅い更新
バンドラーベースのビルド設定でファイルが編集された場合、明らかな理由からバンドル全体を再構築するのは非効率的です。更新速度はアプリのサイズに比例して低下します。
一部のバンドラーでは、開発サーバーはバンドルをメモリ内で実行するため、ファイルが変更されたときにモジュールグラフの一部のみを無効にするだけで済みますが、それでもバンドル全体を再構築し、Webページをリロードする必要があります。バンドルの再構築はコストがかかり、ページをリロードするとアプリケーションの現在の状態が失われます。これが、一部のバンドラーがホットモジュールリプレイスメント (HMR) をサポートしている理由です。これにより、ページの残りの部分に影響を与えることなく、モジュールが「ホットリプレイス」できるようになります。これはDXを大幅に改善しますが、実際には、アプリケーションのサイズが大きくなるにつれて、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と他のアンバンドルビルドツールとの関係は?
PreactチームによるWMRも同様の機能セットを提供しようとしていました。Viteの開発とビルドのためのユニバーサルRollupプラグインAPIは、WMRにインスパイアされています。WMRはもはやメンテナンスされていません。Preactチームは現在、@preactjs/preset-viteと共にViteを推奨しています。
Snowpackもまた、Viteと非常に似た範囲の、バンドルなしのネイティブESM開発サーバーでした。Viteの依存関係の事前バンドルも、Snowpack v1(現在は`esinstall`)にインスパイアされています。Snowpackはもはやメンテナンスされていません。Snowpackチームは現在、Viteを搭載した静的サイトビルダーであるAstroに取り組んでいます。
@web/dev-server(旧`es-dev-server`)は素晴らしいプロジェクトであり、Vite 1.0のKoaベースのサーバーセットアップはそれに影響を受けています。`@web`傘下プロジェクトは活発にメンテナンスされており、Viteユーザーにも利益をもたらす可能性のある他の多くの優れたツールが含まれています。