SPA モード
Remix の基本的な考え方は、サーバーアーキテクチャをあなたが所有することです。そのため、Remix は Web Fetch API をベースに構築され、組み込みまたはコミュニティ提供のアダプターを通じて、あらゆる最新の ランタイム 上で実行できます。 ほとんど のアプリにとって、サーバーは最高の UX/パフォーマンス/SEO などを提供すると考えていますが、現実世界では、シングルページアプリケーションに適したユースケースも数多く存在することは間違いありません。
- サーバーを管理したくない、または GitHub Pages や他の CDN に静的ファイルとしてアプリをデプロイしたい
- Node.js サーバーを実行したくない
- React Router アプリを Remix に移行したい
- サーバーレンダリングできない特殊なタイプの埋め込みアプリを開発している
- 「あなたのボスは SPA アーキテクチャの UX の限界を気にしておらず、開発チームにリアーキテクチャのための時間/能力を与えてくれない」 - Kent C. Dodds
これが、2.5.0 (RFC) で SPA モード のサポートを追加した理由です。これは、クライアントデータ API をベースに構築されています。
SPA モードとは?
SPA モードは、基本的に createBrowserRouter
/RouterProvider
を使用して React Router + Vite を独自に設定した場合と同じですが、いくつかの追加の Remix 機能も備えています。
- ファイルベースのルーティング(または
routes()
を使用した設定ベース) route.lazy
を使用したルートベースのコード分割の自動化- ルートモジュールを事前に読み込むための
<Link prefetch>
のサポート - Remix
<Meta>
/<Links>
API を使用した<head>
の管理
SPA モードは、Remix に実行時に Remix サーバーを実行しないことを伝え、ビルド時に静的な index.html
ファイルを生成し、データの読み込みと変更には クライアントデータ API だけを使用することを示します。
index.html
は、root.tsx
ルートの HydrateFallback
コンポーネントから生成されます。index.html
を生成するための最初の「レンダリング」には、ルートよりも深いルートは含まれません。これにより、index.html
ファイルが、/
(つまり /about
)を超えたパスに対して提供/ハイドレートされるように、CDN/サーバーを設定できます。
使用方法
リポジトリの SPA モードテンプレートを使用して、すぐに開始できます。
または、Remix + Vite アプリで、Remix Vite プラグインの設定で ssr: false
を設定することで、手動で SPA モードを有効にすることができます。
開発
SPA モードでは、従来の Remix SSR アプリと同じように開発を行い、HMR/HDR を有効にするために実行中の Remix dev サーバーを使用します。
プロダクション
SPA モードでアプリをビルドすると、Remix は /
ルートのサーバーハンドラーを呼び出し、レンダリングされた HTML をクライアント側の資産(デフォルトでは build/client/index.html
)とともに index.html
ファイルに保存します。
プレビュー
vite preview を使用して、ローカルでプロダクションビルドをプレビューできます。
vite preview
はプロダクションサーバーとして使用するための設計ではありません。
デプロイ
デプロイするには、任意の HTTP サーバーからアプリを提供できます。サーバーは、単一のルート /index.html
ファイルから複数のパスを提供するように構成する必要があります(一般的には「SPA フォールバック」と呼ばれます)。この機能を直接サポートしていないサーバーの場合、追加の手順が必要になる場合があります。
簡単な例として、sirv-cli を使用できます。
または、express
サーバーで提供する場合(ただし、その時点で Remix を SSR モードで実行することを検討する必要があるかもしれません 😉)。
ドキュメント全体ではなく、div をハイドレートする
HTML ドキュメント全体をハイドレートしたくない場合は、SPA モードを使用して <div id="app">
などのドキュメントのサブセクションだけをハイドレートできます。変更はわずかです。
1. index.html
ファイルを追加する
Remix は HTML ドキュメントをレンダリングしないため、HTML ドキュメントを外部から提供する必要があります。最も簡単な方法は、app/index.html
ドキュメントを保持し、ビルド時に Remix でレンダリングされた HTML を置換できるプレースホルダーを保持して、最終的な index.html
を生成することです。
<!-- Remix SPA -->
の HTML コメントは、Remix の HTML で置換されます。
2. root.tsx
を更新する
<div id="app">
のコンテンツだけをレンダリングするようにルートルートを更新します。
3. entry.server.tsx
を更新する
app/entry.server.tsx
ファイルでは、Remix でレンダリングされた HTML を静的な app/index.html
ファイルのプレースホルダーに挿入する必要があります。また、デフォルトの entry.server.tsx
ファイルのように、<!DOCTYPE html>
宣言を先頭に付けるのをやめる必要があります。これは app/index.html
ファイルに含まれるべきです。
app/entry.server.tsx
ファイルがない場合は、npx remix reveal
を実行する必要があるかもしれません。
4. entry.client.tsx
を更新する
app/entry.client.tsx
を更新して、ドキュメントではなく <div id="app">
をハイドレートします。
app/entry.client.tsx
ファイルがない場合は、npx remix reveal
を実行する必要があるかもしれません。
注釈/注意点
-
SPA モードは、Vite と Remix Vite プラグイン を使用した場合にのみ機能します
-
headers
、loader
、action
などのサーバー API は使用できません。ビルド時にエラーが発生します -
SPA モードでは、
root.tsx
からHydrateFallback
だけをエクスポートできます。他のルートからエクスポートすると、ビルド時にエラーが発生します -
clientLoader
/clientAction
メソッドからserverLoader
/serverAction
を呼び出すことはできません。実行中のサーバーがないため、呼び出されると実行時エラーが発生します
サーバービルド
Remix SPA モードでは、ビルド時にサーバーでルートルートを「事前レンダリング」することによって、index.html
ファイルが生成されることに注意することが重要です
- これは、SPA を作成している場合でも、「サーバービルド」と「サーバーレンダリング」の手順が必要になるため、
document
、window
、localStorage
などのクライアント側の側面を参照する依存関係を使用する際には注意する必要があることを意味します。 - 通常、これらの問題を解決する方法は、
entry.client.tsx
からブラウザ専用のライブラリをインポートして、サーバービルドに含まれないようにすることです - そうでない場合は、
React.lazy
またはremix-utils
の<ClientOnly>
コンポーネントを使用して、通常これらの問題を解決できます。
CJS/ESM 依存関係の問題
アプリの依存関係で ESM/CJS の問題が発生している場合は、Vite の ssr.noExternal オプションを使用して、特定の依存関係をサーバーバンドルに含める必要があるかもしれません。
これらの問題は、通常、公開されたコードが CJS/ESM に対して正しく設定されていない依存関係が原因です。ssr.noExternal
に特定の依存関係を含めることで、Vite は依存関係をサーバービルドにバンドルし、サーバーの実行時にインポートの問題を回避するのに役立ちます。
逆の場合で、特定の依存関係をバンドルから外部に保ちたい場合は、反対の ssr.external
オプションを使用できます。
React Router からの移行
SPA モードは、既存の React Router アプリを Remix アプリ(SPA かどうかは問いません)に移行する際にも役立つと予想されます。
この移行の最初のステップは、現在の React Router アプリを vite
で実行することです。これにより、非 JS コード(CSS、SVG など)に必要なプラグインがすべて揃います。
現在 BrowserRouter
を使用している場合
vite
を使用したら、このガイド の手順に従って、BrowserRouter
アプリをキャッチオールの Remix ルートにドロップできます。
現在 RouterProvider
を使用している場合
現在 RouterProvider
を使用している場合は、ルートを個別のファイルに移動し、route.lazy
を使用して読み込むのが最適な方法です。
- Remix ファイルの規約に従ってこれらのファイルに名前を付けると、Remix(SPA)への移動が容易になります。
- ルートコンポーネントを、名前付きの
Component
エクスポート(RR 用)とdefault
エクスポート(最終的に Remix で使用するため)の両方としてエクスポートします。
ルートがすべて独自のファイルに存在するようになったら、次のようにできます。
- これらのファイルを Remix の
app/
ディレクトリに移動します。 - SPA モードを有効にします。
- すべての
loader
/action
関数をclientLoader
/clientAction
に名前変更します。 - React Router の
index.html
ファイルを、default
コンポーネントとHydrateFallback
をエクスポートするapp/root.tsx
ルートに置き換えます。