Single Page App (SPA)
本頁面內容

Single Page App (SPA)

有兩種方式可以使用 React Router 發布 single page app

  • 作為函式庫 - 您可以在您自己的 SPA 架構中將 React Router 作為函式庫使用,而不是使用 React Router 的框架功能。請參閱將 React Router 作為函式庫指南。
  • 作為框架 - 本指南將著重於此

總覽

當使用 React Router 作為框架時,您可以透過在您的 react-router.config.ts 檔案中設定 ssr:false 來啟用「SPA 模式」。這將停用執行階段伺服器渲染,並在建置時產生一個 index.html,您可以將其作為 SPA 提供和 hydration。

典型的 Single Page app 會傳送幾乎空白的 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 的進入點提供。 這將只渲染根路由,使其能夠在執行階段為應用程式中的任何路徑進行 hydration。

為了提供比空的 <div> 更好的載入 UI,您可以將 HydrateFallback 元件新增至您的根路由,以便在建置時將您的載入 UI 渲染到 index.html 中。 這樣一來,在 SPA 載入/hydration 時,它會立即向使用者顯示。

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 將在建置時呼叫,並且資料將透過選用的 HydrateFallback loaderData 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. 使用客戶端 loader 和客戶端 action

在停用伺服器渲染的情況下,您仍然可以使用 clientLoaderclientAction 來管理路由資料和變更。

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