Vite
Vite は、JavaScript プロジェクトのための強力で高性能で拡張性のある開発環境です。Remix のバンドリング機能を改善し拡張するために、私たちは今Viteをもう一つのコンパイラとしてサポートしています。近い将来、Viteがデフォルトのコンパイラになるでしょう。
クラシックなRemixコンパイラvsRemixのVite
既存のRemixコンパイラは、remix build
およびremix dev
CLIコマンドで利用でき、remix.config.js
で設定されています。これは「クラシックなRemixコンパイラ」と呼ばれるようになりました。
Remix Viteプラグインとremix vite:build
およびremix vite:dev
CLIコマンドを総称して「Remix Vite」と呼びます。
今後、特に記載がない限り、ドキュメントはRemix Viteの使用を前提とします。
はじめに
様々なVite ベースのテンプレートをご用意しています。
これらのテンプレートには、Remix Viteプラグインが設定されたvite.config.ts
ファイルが含まれています。
設定
Remix Viteプラグインは、プロジェクトのルートにあるvite.config.ts
ファイルで設定されます。詳細はVite設定のドキュメントをご覧ください。
Cloudflare
Cloudflareを使い始めるには、cloudflare
テンプレートを使うと良いでしょう:
Viteでローカルで実行する2つの方法があります:
Viteはより良い開発体験を提供しますが、Wranglerはクラウドワーカーのworkerd
ランタイムで実行することで、Cloudflare環境をより忠実に模擬できます。
Cloudflareプロキシ
Viteでクラウドワーカー環境をシミュレーションするために、Wranglerはローカルのworkerd
バインディングへのNodeプロキシを提供しています。
Remix のCloudflareプロキシプラグインはこれらのプロキシを設定してくれます:
これにより、loader
やaction
関数内でcontext.cloudflare
から使えるようになります:
Cloudflare のgetPlatformProxy
ドキュメントで、これらのプロキシについてさらに詳しく確認できます。
バインディング
ローカル開発でのViteやWranglerの設定にはwrangler.tomlを、デプロイ時にはCloudflareダッシュボードのCloudflare Pages バインディングを使います。
wrangler.toml
ファイルを変更したら、必ずwrangler types
を実行してバインディングを再生成する必要があります。
その後、context.cloudflare.env
からバインディングにアクセスできます。
例えば、KVネームスペースがMY_KV
としてバインドされている場合:
ロードコンテキストの拡張
ロードコンテキストに追加のプロパティを追加したい場合は、共有モジュールからgetLoadContext
関数をエクスポートする必要があります。これにより、Vite、Wrangler、Cloudflare Pagesでロードコンテキストが一貫して拡張されます:
functions/[[path]].ts
の両方でgetLoadContext
を渡す必要があります。そうしないと、アプリの実行方法によってロードコンテキストの拡張が一貫していません。
まず、Vite設定でCloudflareプロキシプラグインにgetLoadContext
を渡して、Viteの実行時にロードコンテキストを拡張します:
次に、Wranglerや Cloudflare Pagesへのデプロイ時にロードコンテキストを拡張するため、functions/[[path]].ts
のリクエストハンドラにgetLoadContext
を渡します:
クライアントとサーバーコードの分離
Viteは、クラシックなRemixコンパイラとは異なる方法でクライアントとサーバーコードを扱います。詳細はクライアントとサーバーコードの分離のドキュメントをご覧ください。
新しいビルド出力パス
Viteがpublic
ディレクトリを扱う方法と、既存のRemixコンパイラとは大きな違いがあります。Viteはpublic
ディレクトリからファイルをクライアントビルドディレクトリにコピーしますが、Remixコンパイラはpublic
ディレクトリを変更せず、別のサブディレクトリ(public/build
)をクライアントビルドディレクトリとして使っていました。
Remix のデフォルトプロジェクト構造をViteの動作に合わせるため、ビルド出力パスが変更されました。assetsBuildDirectory
とserverBuildDirectory
のオプションが廃止され、単一のbuildDirectory
オプションに統一されました。デフォルトでは、サーバーはbuild/server
に、クライアントはbuild/client
にビルドされます。
これに伴い、以下のデフォルト設定も変更されています:
- publicPathはViteの"base"オプションに置き換えられ、デフォルトは
"/"
になりました (以前は"/build/"
でした)。 - serverBuildPathは
serverBuildFile
に置き換えられ、デフォルトは"index.js"
になりました。このファイルは、設定したbuildDirectory
内のサーバーディレクトリに書き込まれます。
RemixがViteに移行する理由の1つは、Remixを採用する際の学習コストを下げることです。 つまり、追加の bundling 機能を使う場合は、Remix のドキュメントではなく ViteのドキュメントとViteプラグインコミュニティを参照してください。
Viteには、既存のRemixコンパイラには含まれていない多くの 機能とプラグインがあります。 これらの機能を使う場合は、以降Viteのみを使うことを意図している必要があります。そうでない場合、既存のRemixコンパイラではアプリをコンパイルできなくなります。
移行
Viteのセットアップ
👉 Viteを開発依存関係としてインストール
Remixはもはやスタンドアロンのコンパイラではなく、Viteプラグインに過ぎないので、Viteにフックアップする必要があります。
👉 Remix アプリのルートに vite.config.ts
を作成し、remix.config.js
を削除
サポートされているRemix設定オプションのサブセットは、プラグインに直接渡す必要があります:
HMR & HDR
Viteは HMR やその他の開発機能のためのロバストなクライアントサイドランタイムを提供するので、<LiveReload />
コンポーネントは不要になりました。Remix Viteプラグインを使って開発する際は、<Scripts />
コンポーネントがViteのクライアントサイドランタイムやその他の開発用スクリプトを自動的に含むようになります。
👉 <LiveReload/>
を削除し、<Scripts />
を残す
TypeScript統合
Viteは様々なファイルタイプのインポートを扱いますが、既存のRemixコンパイラとは異なる方法で行う場合があるので、@remix-run/dev
の古い型ではなく、vite/client
の型を参照する必要があります。
vite/client
が提供する型は@remix-run/dev
に暗黙的に含まれる型と互換性がないため、TypeScript設定でskipLibCheck
フラグを有効にする必要があります。将来的にViteプラグインがデフォルトのコンパイラになれば、Remixはこのフラグを必要としなくなるでしょう。
👉 tsconfig.json
を更新
tsconfig.json
のtypes
フィールドを更新し、skipLibCheck
、module
、moduleResolution
が適切に設定されていることを確認してください。
👉 remix.env.d.ts
の更新/削除
remix.env.d.ts
から以下の型宣言を削除します
Remix App Serverからの移行
開発時にremix-serve
を使っていた場合 (またはremix dev
に-c
フラグをつけていなかった場合)、新しいミニマルなdevサーバーに切り替える必要があります。
これは Remix Viteプラグインに組み込まれており、remix vite:dev
を実行すると自動的に起動します。
Remix ViteプラグインはグローバルなNode polyfillをインストールしないので、remix-serve
に依存していた場合は自分でインストールする必要があります。これは、Vite設定の冒頭でinstallGlobals
を呼び出すのが一番簡単です。
Viteのdevサーバーのデフォルトポートはremix-serve
とは異なるので、同じポートを維持したい場合は、Viteのserver.port
オプションで設定する必要があります。
また、新しいビルド出力パスに合わせて更新する必要があります。サーバーはbuild/server
に、クライアントアセットはbuild/client
にビルドされます。
👉 dev
、build
、start
スクリプトを更新
👉 グローバルなNode polyfillをVite設定に追加
👉 Viteのdevサーバーポートを設定(オプション)
カスタムサーバーの移行
カスタムサーバーを開発時に使っていた場合、カスタムサーバーを編集して Viteのconnect
ミドルウェアを使う必要があります。
これにより、開発中にアセットリクエストと初期レンダリングリクエストをViteにデリゲートできるので、Viteの優れたDXを活用できます。
その上で、開発時に"virtual:remix/server-build"
という仮想モジュールをロードして、Viteベースのリクエストハンドラを作成できます。
また、サーバーコードを更新して新しいビルド出力パスを参照する必要がああります。サーバービルドはbuild/server
に、クライアントアセットはbuild/client
にビルドされます。
Expressを使っていた場合の例は以下の通りです。
👉 server.mjs
ファイルを更新
👉 build
、dev
、start
スクリプトを更新
TypeScriptでカスタムサーバーを書きたい場合は、tsx
やtsm
などのツールを使ってサーバーを実行できます:
ただし、サーバー起動時の初期遅延が若干目立つ可能性があります。
Cloudflareファンクションの移行
Remix Viteプラグインは、フルスタックアプリケーション向けに設計されたCloudflare Pagesのみをサポートしています。Cloudflare Workers Sitesをご利用の場合は、Cloudflare Pagesへの移行ガイドをご覧ください。
👉 remix
プラグインの前にcloudflareDevProxyVitePlugin
を追加して、Viteのdevサーバーのミドルウェアを適切に上書きする!
アプリケーションでは、Remix設定のserver
フィールドを使ってCatchallのCloudflareファンクションを生成していた可能性があります。
Viteでは、このような間接的な方法は不要になりました。
代わりに、Express用やその他のカスタムサーバー用と同じように、Cloudflare用の Catchall ルートを直接記述できます。
👉 Remixの Catchall ルートを作成
👉 バインディングと環境変数はcontext.cloudflare.env
から、context.env
ではなく
主に開発時にはViteを使いますが、Wranglerでプレビューやデプロイすることもできます。
詳細は、このドキュメントのCloudflareセクションをご覧ください。
👉 package.json
のスクリプトを更新
ビルド出力パスの参照の更新
既存のRemixコンパイラのデフォルトオプションを使っていた場合、サーバーはbuild
に、クライアントはpublic/build
にビルドされていました。Viteがpublic
ディレクトリを扱う方法と、既存のRemixコンパイラとは異なるため、これらの出力パスが変更されました。
👉 ビルド出力パスの参照を更新
- サーバーは現在、デフォルトで
build/server
にビルドされます。 - クライアントは現在、デフォルトで
build/client
にビルドされます。
例えば、Blues StackのDockerfileを更新する場合:
パスエイリアスの設定
Remixコンパイラは、tsconfig.json
のpaths
オプションを活用してパスエイリアスを解決していました。Remixコミュニティでは、app
ディレクトリに~
というエイリアスを定義するのが一般的でした。
Viteはデフォルトではパスエイリアスを提供していません。この機能に依存していた場合は、vite-tsconfig-pathsプラグインを使ってRemixコンパイラと同様の動作を実現できます:
👉 vite-tsconfig-paths
をインストール
👉 Vite設定にvite-tsconfig-paths
を追加
@remix-run/css-bundle
の削除
Viteには、CSS Side Effect インポート、PostCSS、CSS Modules など、CSS バンドリングの機能が組み込まれています。Remix Viteプラグインは、自動的にバンドルされたCSSをリレバントなルートに適用します。
@remix-run/css-bundle
cssBundleHref
エクスポートは常にundefined
になるからです。
👉 @remix-run/css-bundle
をアンインストール
👉 cssBundleHref
の参照を削除
ルートのlinks
関数がcssBundleHref
を設定するためだけに使われている場合は、完全に削除できます。
links
で参照されるCSSインポートを修正
links
関数でCSSを参照している場合、対応するCSSインポートをViteの明示的な?url
インポート構文に更新する必要があります。
👉 links
で使われているCSSインポートに?url
を追加
.css?url
インポートには、Vite v5.1以降が必要です
TailwindにPostCSSを使って有効化
プロジェクトが Tailwind CSS を使っている場合、Remix コンパイラでは tailwind
オプションを有効にするだけで設定が不要でした。
しかし、Viteの場合は明示的にPostCSSの設定ファイルが必要です。
👉 PostCSSの設定ファイルがない場合は追加し、tailwindcss
プラグインを含める
プロジェクトにすでにPostCSSの設定ファイルがある場合は、tailwindcss
プラグインが含まれていない可能性があります。
これは、Remixコンパイラの tailwind
設定オプションが有効だった場合、自動的に含まれていたためです。
👉 PostCSSの設定にTailwindプラグインが含まれていない場合は追加
👉 Tailwind CSS インポートの移行
links
関数でTailwind CSSファイルを参照している場合、Tailwind CSSインポートステートメントを移行する必要があります。
Vanilla Extractプラグインの追加
Vanilla Extractを使っている場合は、Viteプラグインを設定する必要があります。
👉 Vanilla Extractの公式Viteプラグインをインストール
👉 Vite設定にVanilla Extractプラグインを追加
MDXプラグインの追加
MDXを使っている場合、ViteのプラグインAPI はRollupのプラグインAPI の拡張なので、公式のMDX Rollupプラグインを使うべきです:
👉 MDX Rollupプラグインをインストール
RemixプラグインはJavaScriptやTypeScriptファイルを処理することを期待しているので、他の言語 (MDXなど) からの変換は先に行う必要があります。 この場合、MDXプラグインをRemixプラグインの前に配置する必要があります。
👉 Vite設定にMDX Rollupプラグインを追加
MDXフロントマターのサポート
Remixコンパイラでは、MDXでフロントマターを定義できました。この機能を使っていた場合、Viteではremark-mdx-frontmatterを使って実現できます。
👉 必要なRemarkフロントマタープラグインをインストール
👉 MDX RollupプラグインにRemarkフロントマタープラグインを渡す
Remixコンパイラでは、フロントマターのエクスポート名はattributes
でしたが、フロントマタープラグインのデフォルトのエクスポート名はfrontmatter
です。エクスポート名を設定することは可能ですが、代わりにアプリコードを更新して、デフォルトのエクスポート名を使うことをお勧めします。
👉 MDXファイル内のMDX attributes
エクスポートをfrontmatter
に変更
👉 MDX attributes
エクスポートをfrontmatter
に変更(consumer側)
MDXファイルの型定義
👉 env.d.ts
にMDX用の型を追加
MDXのフロントマターをルートエクスポートにマッピング
Remixコンパイラでは、フロントマターからheaders
、meta
、handle
ルートエクスポートを定義できました。
この Remix 固有の機能は、remark-mdx-frontmatter
プラグインではサポートされていません。
この機能を使っていた場合は、手動でフロントマターをルートエクスポートにマッピングする必要があります:
👉 MDXルートでフロントマターをルートエクスポートにマッピング
注意点として、MDXのルートエクスポートを明示的にマッピングすることで、好きな形式のフロントマターを使うことができるようになります。
MDXファイル名の使用の更新
Remixコンパイラでは、すべてのMDXファイルからfilename
エクスポートが提供されていました。
これは主に、MDXルートのコレクションにリンクする際に使われていました。
この機能を使っていた場合、Viteではグローブインポートを使うと、ファイル名をモジュールにマッピングする便利なデータ構造が得られます。
これにより、個々のMDXファイルをすべて手動でインポートする必要がなくなります。
例えば、posts
ディレクトリ内のすべてのMDXファイルをインポートするには:
これは、以下のように個々にインポートするのと同等です:
すぐにMDXファイルをインポートしたい場合は、以下のようにできます:
デバッグ
NODE_OPTIONS
環境変数を使ってデバッグセッションを開始できます:
次に、ブラウザからデバッガーを接続できます。
例えば、Chromeでは chrome://inspect
を開いたり、開発者ツールのNodeJSアイコンをクリックするとデバッガーに接続できます。
vite-plugin-inspect
vite-plugin-inspect
は、各Viteプラグインがコードをどのように変換しているか、およびプラグインごとの所要時間を表示してくれます。
パフォーマンス
Remixには--profile
フラグがあり、パフォーマンスプロファイリングが可能です。
--profile
モードで実行すると、.cpuprofile
ファイルが生成されます。これをspeedscope.appにアップロードして分析できます。
開発中も、p + enter
を押してプロファイリングセッションを開始/停止できます。
開発サーバーの起動時にプロファイリングを行いたい場合は、--profile
フラグを使います:
Viteのパフォーマンスドキュメントも参考にしてください!
バンドル分析
バンドルを視覚化、分析するには、rollup-plugin-visualizerプラグインを使えます:
remix vite:build
を実行すると、各バンドルにstats.html
ファイルが生成されます:
build
├── client
│ ├── assets/
│ ├── favicon.ico
│ └── stats.html 👈
└── server
├── index.js
└── stats.html 👈
stats.html
をブラウザで開いて、バンドルの分析ができます。
トラブルシューティング
デバッグとパフォーマンスのセクションで、一般的なトラブルシューティングのヒントを確認してください。 また、githubのRemix Viteプラグインの既知の問題で、同様の問題が報告されていないかチェックしてください。
HMR
ホットアップデートを期待しているのにフルページリロードが発生する場合は、ホットモジュール置換に関する議論を確認し、一般的な問題への対処方法を学んでください。
ESM / CJS
ViteはESMとCJSの両方のディペンデンシーをサポートしますが、時々ESM/CJSの相互運用性の問題に遭遇することがあります。 通常、これはディペンデンシーがESMをうまくサポートしていないためです。 そしてそれは非常に難しい問題なので、私たちも彼らを非難するつもりはありませんESMとCJSの両立は本当に難しい。
例の不具合を修正する方法の詳細については、🎥 How to Fix CJS/ESM Bugs in Remixをご覧ください。
ディペンデンシーが正しく設定されていないかどうかを診断するには、publintやAre The Types Wrongを使ってください。
さらに、Viteのssr.noExternal
オプションを使って、Remixコンパイラの serverDependenciesToBundle
と同様の動作をEmulateするこ
ともできます。
開発中にブラウザにサーバーコードのエラーが表示される
開発中にブラウザのコンソールにサーバーコードに関するエラーが表示される場合は、明示的にサーバー専用コードを分離する必要があります。 例えば、以下のようなエラーが表示された場合:
process
などのサーバー専用のグローバル変数を使っているモジュールを特定し、別の.server
モジュールやvite-env-only
を使って隔離する必要があります。
Viteはロールアップを使ってコードをツリーシェイクするので、これらのエラーは開発中にのみ発生します。
他のViteベースのツールでのプラグインの使用 (Vitest、Storybookなど)
Remix Viteプラグインは、アプリケーションの開発サーバーと本番ビルドでのみ使用することを意図しています。 一方、Vitest やStorybookなどの他のViteベースのツールもVite設定ファイルを使用しますが、Remix Viteプラグインはこれらのツールと連携するように設計されていません。 現時点では、他のViteベースのツールと併用する際は、プラグインを除外することをお勧めします。
Vitest の場合:
Storybookの場合:
または、ツールごとに別のVite設定ファイルを使うこともできます。 Remixに特化したVite設定ファイルを使う例:
Remix Viteプラグインを提供しない場合は、Vite Plugin Reactを自分で提供する必要がある
ドキュメントがリマウントされるときにスタイルが消える問題
Remix では、ドキュメント全体をReactで描画しています。この際、head
要素に動的に要素が挿入されると問題が発生する可能性があります。ドキュメントが再マウントされると、既存のhead
要素が削除され新しいものに置き換わるため、Viteが開発中に注入したstyle
要素が削除されてしまうのです。
これは Reactの既知の問題で、canary版で修正されています。リスクを理解した上で、特定のReactバージョンにピンポイントでアプリを固定し、パッケージのオーバーライドを使ってプロジェクト全体で同じバージョンのReactを使うことができます。例:
この Viteによって開発中に注入されたスタイルの問題は、本番ビルドでは発生しません。なぜなら、静的なCSSファイルが生成されるからです。
Remixでは、ルートコンポーネントのデフォルトエクスポートと、そのErrorBoundaryやHydrateFallbackエクスポートの間で描画が切り替わると、新しいドキュメントレベルのコンポーネントがマウントされるため、この問題が表面化する可能性があります。
hydrationエラーも同様の問題を引き起こす可能性があります。hydrationエラーはアプリのコードが原因である場合もありますし、ブラウザ拡張機能によってドキュメントが書き換えられることが原因になることもあります。
これがViteに関連するのは、開発時にViteがCSSインポートをJSファイルに変換し、それらを副作用としてドキュメントにインジェクションしているためです。これにより、遅延読み込みやCSSファイルのHMRをサポートしています。
例えば、アプリに以下のようなCSSファイルがあるとします:
開発時、このCSSファイルは以下のようなJavaScriptコードに変換されます:
この変換は本番コードには適用されないため、この問題は開発時にのみ発生します。
開発時のWranglerエラー
Cloudflare Pagesを使っている場合、wrangler pages dev
から以下のようなエラーが発生することがあります:
これはWranglerの既知の問題です。
謝辞
Viteは素晴らしいプロジェクトで、Viteチームの皆様に感謝しています。 特に、Matias Capeletto、Arnaud Barré、Bjorn Luのようなメンバーからの指導に感謝します。
RemixコミュニティもすぐにViteサポートを探求してくれ、その貢献に感謝しています:
最後に、他のフレームワークがViteサポートをどのように実装したかにも触発されました: