单页应用 (SPA)
本页内容

单页应用 (SPA)

使用 React Router 部署单页应用有两种方式

  • 作为库使用 - 您可以不在使用 React Router 的框架特性,而是在您自己的 SPA 架构中将其作为库使用。请参阅将 React Router 作为库使用指南。
  • 作为框架使用 - 本指南将重点介绍此方式

概览

当使用 React Router 作为框架时,您可以在您的 react-router.config.ts 文件中设置 ssr:false 来启用“SPA 模式”。这将禁用运行时服务器渲染,并在构建时生成一个 index.html 文件,您可以将其作为 SPA 进行服务和水合。

典型的单页应用会发送一个大部分为空的 index.html 模板,其中内容很少,通常只有一个空的 <div id="root"></div>。相比之下,react-router build (在 SPA 模式下) 会在构建时将您的根路由预渲染到 index.html 文件中。这意味着您可以:

  • 发送的内容不仅仅是一个空的 <div>
  • 使用根路由的 loader 为您的应用外壳加载数据
  • 使用 React 组件生成用户看到的初始页面 (根路由的 HydrateFallback)
  • 稍后可以重新启用服务器渲染,而无需更改您的 UI

重要的是要注意,设置 ssr:false *仅*禁用*运行时服务器渲染*。React Router 仍然会在*构建时*服务器渲染您的根路由,以生成 index.html 文件。这就是为什么您的项目仍然需要依赖于 @react-router/node 并且您的路由需要是 SSR 安全的。这意味着您不能在初始渲染期间调用 window 或其他仅浏览器可用的 API,即使禁用了服务器渲染。

SPA 模式是一种特殊的“预渲染”形式,它允许您从同一个 HTML 文件中提供应用的所有路径。如果您想进行更广泛的预渲染,请参阅预渲染指南。

1. 禁用运行时服务器渲染

服务器渲染默认是启用的。在 react-router.config.ts 中将 ssr 标志设置为 false 即可禁用它。

import { type Config } from "@react-router/dev/config";

export default {
  ssr: false,
} satisfies Config;

将此设置设为 false 后,将不再生成服务器构建产物。

重要的是要注意,设置 ssr:false *仅*禁用*运行时服务器渲染*。React Router 仍然会在*构建时*服务器渲染您的根路由,以生成 index.html 文件。这就是为什么您的项目仍然需要依赖于 @react-router/node 并且您的路由需要是 SSR 安全的。这意味着您不能在初始渲染期间调用 window 或其他仅浏览器可用的 API,即使禁用了服务器渲染。

2. 为根路由添加 HydrateFallback 及可选的 loader

SPA 模式会在构建时生成一个 index.html 文件,您可以将其作为 SPA 的入口点进行服务。这只会渲染根路由,以使其能够在运行时为应用中的任何路径进行水合。

为了提供比空的 <div> 更好的加载 UI,您可以为您的根路由添加一个 HydrateFallback 组件,以在构建时将您的加载 UI 渲染到 index.html 中。这样一来,当 SPA 正在加载/水合时,它将立即显示给用户。

import LoadingScreen from "./components/loading-screen";

export function Layout() {
  return <html>{/*...*/}</html>;
}

export function HydrateFallback() {
  return <LoadingScreen />;
}

export default function App() {
  return <Outlet />;
}

由于根路由是在构建时进行服务器渲染的,如果您愿意,您也可以在根路由中使用一个 loader。这个 loader 将在构建时被调用,并且数据将通过可选的 HydrateFallbackloaderData prop 提供。

import { Route } from "./+types/root";

export async function loader() {
  return {
    version: await getVersion(),
  };
}

export function HydrateFallback({
  loaderData,
}: Route.ComponentProps) {
  return (
    <div>
      <h1>Loading version {loaderData.version}...</h1>
      <AwesomeSpinner />
    </div>
  );
}

在使用 SPA 模式时,您不能在应用中的任何其他路由中包含 loader,除非您正在预渲染这些页面

3. 使用客户端加载器 (client loaders) 和客户端 action (client actions)

禁用服务器渲染后,您仍然可以使用 clientLoaderclientAction 来管理路由数据和 mutation。

import { Route } from "./+types/some-route";

export async function clientLoader({
  params,
}: Route.ClientLoaderArgs) {
  let data = await fetch(`/some/api/stuff/${params.id}`);
  return data;
}

export async function clientAction({
  request,
}: Route.ClientActionArgs) {
  let formData = await request.formData();
  return await processPayment(formData);
}

4. 将所有 URL 指向 index.html

运行 react-router build 后,将 build/client 目录部署到您喜欢的任何静态主机。

部署任何 SPA 的常见做法是,您需要配置您的主机,将所有 URL 指向客户端构建产物的 index.html。有些主机默认执行此操作,但有些则不然。例如,某些主机可能支持通过 _redirects 文件来实现这一点

/*    /index.html   200

如果您在应用的有效路由上遇到 404 错误,很可能您需要配置您的主机。

文档和示例 CC 4.0