Vite 8は2026年3月12日にリリースされた。その2日後、筆者は8つのSvelteKitプロジェクトを移行し終えた。モノレポとスタンドアロンの混在構成で、すべてCloudflare Workersにデプロイしている。移行はほぼ機械的な作業で、ビルドも速くなった。いくつかの教訓を共有する。
Vite 8で何が変わるのか
Vite 8は、従来の「分裂型」バンドラアーキテクチャを置き換えるものだ。Vite 7では、開発時のトランスフォームと依存関係の最適化にesbuildを使い、本番ビルドではRollupに切り替えていた。2つの異なるツールが重複する仕事をし、それぞれの環境で微妙に異なる挙動を示していた。
Vite 8ではRolldownに統一される。RolldownはRustベースのバンドラで、OxcがJavaScriptのトランスフォームとミニファイを担い(esbuildの役割を置き換え)、Lightning CSSがCSSのミニファイを担当する。バンドラが1つになることで、開発と本番の挙動が一致する。
では、Vite 8は実際に良いのか。結論として、良い。CloudflareのUIで各Workerを確認したところ、ほとんどのプロジェクトで全体ビルド時間が約15秒短縮され、2つのプロジェクトでは45〜60秒短縮された。割合にすると概ね10〜15%の改善で、一部は40〜50%の短縮だ。ただし注意点がある。Vite 8の改善はビルドの「バンドリング」フェーズに限られる。ビルド時間の大部分がViteプラグイン(Sentry、SvelteKitのプリプロセッシングなど)に費やされている場合、改善幅は小さくなる。Cloudflareの処理速度のばらつきなど他の要因もあるため厳密な計測ではないが、ビルド時間の短縮は誰にとっても歓迎すべきことだ。
移行の実際
Viteの内部をカスタマイズしていないSvelteKitプロジェクトの場合、移行は4つのコマンドで完了する。
pnpm update vite@^8.0.0 --recursive
pnpm update @sveltejs/kit@latest --recursive
pnpm update @sveltejs/vite-plugin-svelte@latest --recursive
pnpm update @sveltejs/adapter-cloudflare@latest --recursive
@sveltejs/vite-plugin-svelteはVite 8対応のためにメジャーバージョンが上がる(6から7へ)。これが見落としやすい重要な依存関係だ。上記のpnpm updateコマンドなら自動的に処理されるが、package.jsonを手動で編集する場合は、バージョン範囲を"^6"から"^7"に変更する必要がある。^6の範囲では7.xに解決されず、Vite 8とのピア依存関係の不一致でビルドが失敗する。adapter-cloudflareの更新はマイナーパッチ(例:7.2.5から7.2.8)だ。
vitestが4.xであることも確認すること。筆者のプロジェクトでは4.0.18で問題なく動作した。vitest 3.xのままの場合、2つのメジャーバージョンアップグレードを同時に行うことになる(vitest 3から4、そしてVite 7から8)。vitest 3.xはVite 8のRolldownバンドラをサポートしていない1。まずVite 7上でvitestを4.xにアップグレードし、テストスイートが通ることを確認してから、Vite 8へのバージョンアップを別ステップで行うことを推奨する。
SvelteKitは@sveltejs/kit@2.53.x以降でVite 8をサポートしている2。ただし、執筆時点の最新マイナー(2.55.0)には追加の修正が含まれているため、そちらの取得を推奨する。更新後、すべてのワークスペースパッケージがVite 8に解決されていることを確認する。
pnpm ls vite --recursive --depth 0
いずれかのパッケージがまだVite 7をピン留めしている場合は、そのpackage.jsonを直接更新する。Turborepoは古いロックファイルのエントリーを自動で修正してはくれない。
設定キーのリネーム
Vite 8ではいくつかの設定キーが非推奨となった。旧名は互換レイヤーを通じてまだ機能するが、今のうちに移行しておけば警告と将来の破壊を避けられる。
| 非推奨 | 置き換え |
|---|---|
build.rollupOptions |
build.rolldownOptions |
worker.rollupOptions |
worker.rolldownOptions |
optimizeDeps.esbuildOptions |
optimizeDeps.rolldownOptions |
esbuild(トップレベル) |
oxc |
一見単純だが注意が必要なリネームがesbuildからoxcへの変更だ。本番ビルドでデバッグ文を除去するためにesbuild: { drop: ['console', 'debugger'] }を使っている場合、置き換え先は別の場所にある。build.rolldownOptions.output.minify.compress.drop*だ3。非推奨のesbuildキーは互換レイヤーで引き続き動作するが、Rolldownネイティブのオプションに移行する方がクリーンだ。微妙な落とし穴がある。非本番モードでdropに空配列[]を渡している場合は、undefinedに変更すること。互換レイヤーはVite 7のesbuildと同じようには空配列を処理しない。
8つのプロジェクトのうち6つはカスタムVite設定がなく、SvelteKitのデフォルトに依存していた。それらの移行は文字通りバージョンバンプだけで、コードや設定の変更はゼロだった。Tailwind CSS v4を@tailwindcss/viteで使っている場合も、Vite 8で変更なしに動作する。2つのプロジェクトでは手動チャンク分割のためのrollupOptionsがあり、対応が必要だった。
manualChunksの削除
Vite 8ではoutput.manualChunksのオブジェクト形式が完全に削除された3。そのため、対応が必要になる場合がある。関数形式は非推奨だ。ベンダーチャンクを明示的に分割していた場合はこうなる。
// Vite 8では動作しない
build: {
rollupOptions: {
output: {
manualChunks: { vendor: ['some-lib'] }
}
}
}
置き換えはRolldownのcodeSplittingオプションだ4。実際には、筆者は2つのプロジェクトから手動チャンク設定を削除し、Rolldownに自動分割を任せた。出力は同等で、チャンク境界は若干変わったが、バンドルサイズの合計はほぼ変わらなかった。
いくつかの落とし穴
CommonJS相互運用:静かな破壊
Vite 8では、CommonJSモジュールからのdefaultインポートの解決方法が開発と本番で統一された3。以前は、import foo from 'cjs-package'とした場合、開発ではmodule.exportsが、本番ではmodule.exports.defaultが返される(あるいはその逆)ことがあった。パッケージのエクスポート構造によって挙動が異なっていたのだ。
これが8プロジェクト中2つで問題になった。症状は、Vite 7では正常に動作していたデフォルトインポートでCannot read properties of undefinedが発生するというものだ。どちらのケースも、曖昧なCommonJSエクスポートを公開している古いnpmパッケージが原因だった。
修正はそれぞれ単純だった。1つのパッケージは明示的なresolve.aliasでESMビルドをターゲットにでき、もう1つはメンテナンスされている代替パッケージに置き換えた。ただし、これらを発見するにはビルドとスモークテストの完全なサイクルが必要だった。型チェックではこれを検出できない。型は正しいが、ランタイムのモジュール解決が変わるためだ。
問題に遭遇した場合、対処中の一時的な回避策はこちらだ。
// 一時的な対処。依存関係の修正までの時間稼ぎ
export default defineConfig({
legacy: {
inconsistentCjsInterop: true
}
});
このフラグを本番に残してはいけない。移行を段階的に行うために存在するものであり、恒久的な設定ではない。
Cloudflare Workers:ほぼ問題なし
Viteのバンドラ変更はWorkersのランタイム動作には影響しない。D1バインディング、KV、R2、サービスバインディングはすべて同じように動作する。ただし、ビルド出力は変わる。Rolldownではチャンクのファイル名とコンテンツハッシュが異なるため、ファイル名でキャッシュバスティングしている場合(そうしているはずだが)、初回デプロイ時に完全なキャッシュ無効化が発生する。
筆者は各プロジェクトをまずプレビュー環境にデプロイした。
npx wrangler versions upload
8プロジェクトすべてが、Workers固有の問題なく初回テストをパスした。adapter-cloudflareの更新はVite 8のビルド出力を正しく処理する。
Cloudflareの設定を触っているついでに、wrangler.jsoncのcompatibility_dateを移行日に更新するのもよいタイミングだ。Viteの変更に厳密には必要ないが、ツールのメジャーアップデートは最新のWorkersランタイム動作を取り込む自然なチェックポイントだ。モノレポでは、SvelteKit以外のWorkerも忘れずに。筆者のワークスペースには7つのwrangler.jsonc / wrangler.tomlファイルがあり、すべて同じコミットで更新した。スケジューラWorkerやSync Workerなど、Viteに関係ないWorkerは見落としやすい。
CSSバンドルサイズ:若干増加
Lightning CSSはesbuildとは異なる方法で構文の低レベル化を処理する。筆者のプロジェクト全体で、CSSバンドルサイズが1〜3%増加した。心配するほどではないが、知っておく価値はある。CSSのバイト数を厳密に管理している場合は、一時的に戻すことができる。
export default defineConfig({
build: {
cssMinify: 'esbuild' // devDependencyとしてesbuildが必要
}
});
筆者は気にしなかった。差は無視できるレベルだ。
不要なオーバーライドの整理
pnpmやnpmのオーバーライドでRollupをパッチ済みバージョンに固定していた場合(例:CVE-2026-27606のパストラバーサル修正)、それらは削除できる。Vite 8はRollupを一切使わないためだ。筆者のプロジェクトには"rollup": ">=4.59.0"というオーバーライドがあり、不要になった。Rollup以外のオーバーライド(cookie、undici、minimatchなど、それぞれのCVEのために固定しているもの)は移行の影響を受けない。
ただし、不要なオーバーライドを削除するだけでは不十分だ。RolldownはRollupとは異なる推移的依存関係ツリーをもたらすため、pnpm auditで以前はなかった新しい発見が報告される。移行後に必ず実行すること。筆者はdevalue(プロトタイプ汚染)、minimatch(ReDoS)、file-type(ASFパーサの無限ループ)、さらにはsvelte自体(SSRのcontenteditableにおけるXSS)に対してリゾリューションを追加する必要があった。メジャーバージョンアップは完全なセキュリティ監査の自然なタイミングだ。オーバーライドの削除と追加の両方を想定しておくこと。ロックファイルのdiffを手動でレビューしようとしないこと。筆者のプロジェクトでは1,200行以上の変更があった。pnpm auditとpnpm ls vite --recursiveを信頼すること。
サブWorkerの放置されたロックファイル
リポジトリのサブディレクトリにCloudflare Workers(メールWorker、CronWorker、Sync Workerなど)がある場合、pnpm-lock.yamlと並んで放置されたpackage-lock.jsonがないか確認すること。Dependabotはルートだけでなく、見つけたすべてのロックファイルをスキャンする。筆者のケースでは、Workerサブディレクトリに初期のnpm installで残ったpackage-lock.jsonが1つあり、pnpmではすでに解決済みの脆弱なundiciバージョンに対して38件のDependabotアラートを生成していた。修正方法は、古いロックファイルを削除し、package-lock.jsonをそのWorkerの.gitignoreに追加し、推移的脆弱性に対して各Workerが独自のpnpm.overridesを持つようにすることだ(ルートのpackage.jsonのpnpmオーバーライドは独立したWorkerのロックファイルには連鎖しない)。
プラグインタイミング警告
移行後、Rolldownがビルド出力でPLUGIN_TIMINGS警告を出力するようになった。各Viteプラグインがビルド時間の何パーセントを消費しているかを表示するものだ。Rollupはこれを報告しなかった。筆者のプロジェクトの1つでは、vite-plugin-sveltekit-guardがビルド時間全体の71%を消費し、実際のバンドリングはごくわずかだった。有用な診断データだ。Vite 8のビルドがあまり速く感じない場合は、プラグインタイミングを確認すること。Rolldownバンドラが60%速くなっても、プラグインがビルドを支配していれば、体感的な改善は見出しの数値より小さくなる。
移行後に有効化すべき機能
Vite 8の新機能のうち、すぐに有効化する価値があると感じたものが2つある。
server.forwardConsole5は、開発中にブラウザのconsole.*呼び出しをターミナルに転送する。デバッグ時に、クライアントサイドのエラーがサーバーログと並んで表示され、ブラウザのDevToolsに切り替える必要がなくなる。1つ注意点がある。純粋なVite開発サーバー(例:pnpm run dev:fast)ではクリーンに動作するが、Cloudflareバインディングのためにwrangler経由で実行する場合(例:pnpm run dev)、wranglerが開発サーバーをプロキシするためコンソール転送の動作が不安定になることがある。それでも有効にする価値はある。wrangler経由でメッセージが一部失われても驚かないこと。
export default defineConfig({
server: {
forwardConsole: true,
},
});
build.license6は、バンドルされたすべての依存関係のライセンスを一覧にした.vite/license.mdを出力する。手間の少ないコンプライアンスドキュメントで、組織がオープンソースの使用状況を追跡している場合に有用だ。
筆者はresolve.tsconfigPaths5(SvelteKitがすでに$libエイリアスを処理しているため)とdevtools: true(まだアルファ版のため)はスキップした。
1つのコツとして、筆者は移行を2つの別々のコミットで行った。まずバージョンバンプとクリーンアップ、次に新機能の有効化だ。「動くようにする」と「より良くする」を分けることで、PRがレビューしやすくなり、後で問題が発生した際のbisectも容易になる。
段階的な移行パス
直接アップグレードで問題が多すぎる場合、2段階のアプローチがある3。Vite 7のままrolldown-viteに切り替えて、Rolldown固有の動作を分離する方法だ。
{
"devDependencies": {
"vite": "npm:rolldown-vite@7.2.2"
}
}
使い慣れたVite 7の基盤上でRolldownの問題を修正してから、Vite 8本体にアップグレードする。これにより「Rolldownの動作変更」と「その他のVite 8の変更」を分離し、それぞれ独立してデバッグできる。筆者はこのアプローチを使う必要はなかったが、存在を知っておくのは有用だ。
移行後の整理
プロジェクトのドキュメントを新しいツール構成に合わせて更新すること。例えば以下の通りだ。
| ツール | 役割 |
|---|---|
| Vite 8 | ビルドツール |
| Rolldown | バンドラ(esbuild + Rollupを置き換え) |
| Oxc | JSトランスフォームとミニファイ |
| Lightning CSS | CSSミニファイ |
SvelteKitガイドでrollupOptionsを参照している箇所があれば、rolldownOptionsに更新すること。モノレポで共有設定を管理している場合は、すべてのワークスペースパッケージでViteのバージョン制約が^8.0.0であることを確認すること。Vite 7のままのパッケージが1つでもあると、解決の競合が発生する。
8つのプロジェクトをVite 8に移行し、ロールバックはゼロだった。スムーズなメジャーアップグレードを実現したViteチームに感謝したい。ビルド速度の改善は常にありがたく、有用なデバッグログも非常に助かる。@sveltejs/kit@2.53.0以降のSvelteKitを使っているなら、待つ理由はない。
Footnotes
-
vitest 4.xでVite 8サポートが追加された。vitestのリリースを参照。 ↩
-
SvelteKitのVite 8サポートは2.53.xシリーズで導入された。What's new in Svelte: March 2026および@sveltejs/kitのリリースを参照。 ↩
-
公式のVite v7からv8への移行ガイドにCJS互換性の変更、設定リネーム、
manualChunks削除、esbuild.dropの置き換え、段階的なrolldown-vite移行パスの詳細がある。 ↩ ↩2 ↩3 ↩4 -
Vite 8のアナウンスに
server.forwardConsole、resolve.tsconfigPaths、その他の新機能の詳細がある。 ↩ ↩2
Comments
Nothing yet. Say the first thing.
Sign in to join the conversation.