前言
createStaticRouter
是 React Router
專為 服務端渲染(SSR) 設計的 API,用于在服務器端處理路由匹配
和數據加載
。它在構建靜態 HTML
響應時替代了客戶端的 BrowserRouter
,確保 SSR
和客戶端 Hydration
的路由狀態一致。
一、createStaticRouter 核心用途
- 服務端路由匹配:根據請求
URL
確定渲染的組件 - 數據預加載:執行路由的
loader
函數獲取初始數據 - 錯誤處理:捕獲渲染過程中的路由級錯誤
SSR/SSG 支持
:生成包含初始數據的靜態 HTML
二、createStaticRouter 使用步驟詳解(配合 Express
示例)
2.1、 定義路由配置
// src/routes.js
import HomePage from "./pages/Home";
import UserPage from "./pages/User";export const routes = [{path: "/",loader: () => fetch("/api/data"), // 數據加載函數element: <HomePage />,errorElement: <ErrorPage /> // 錯誤邊界},{path: "/user/:id",loader: ({ params }) => fetch(`/api/users/${params.id}`),element: <UserPage />}
];
2.2、 服務端路由處理
// server.js
import express from "express";
import { createStaticRouter,StaticRouterProvider
} from "react-router-dom/server";
import { routes } from "./src/routes";const app = express();app.use("*", async (req, res) => {// 1. 創建請求感知的靜態路由const router = createStaticRouter(routes, {basename: "/app", // 基礎路徑location: req.originalUrl // 當前請求路徑});// 2. 觸發所有匹配路由的 loaderconst promises = router.matches.map(match => match.route.loader?.({ request: req, params: match.params }));// 3. 等待數據加載完成const loaderData = await Promise.all(promises);// 4. 將數據注入路由上下文const context = {loaderData,errors: null // 可捕獲 loader 錯誤};// 5. 渲染為 HTML 字符串const html = ReactDOMServer.renderToString(<StaticRouterProvider router={router} context={context} />);// 6. 拼接完整 HTML 響應res.send(`<html><body><div id="root">${html}</div><script>// 注入初始數據供客戶端 Hydration 使用window.__STATIC_CONTEXT = ${JSON.stringify(context)};</script></body></html>`);
});
2.3、 客戶端 Hydration
// src/client.js
import { hydrateRoot } from "react-dom/client";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import { routes } from "./routes";// 復用路由配置
const router = createBrowserRouter(routes, {basename: "/app",hydrationData: window.__STATIC_CONTEXT // 注入服務端數據
});hydrateRoot(document.getElementById("root"),<RouterProvider router={router} />
);
三、createStaticRouter關鍵配置說明
參數 作用
basename
: 應用基礎路徑(e.g. /app)
location
: 當前請求 URL 對象 (必需)router.matches
: 當前 URL 匹配的路由對象數組context.loaderData
: 存儲 loader 返回數據的數組,索引與 router.matches 順序一致
四、createStaticRouter 錯誤處理機制
// 在路由配置中添加錯誤邊界
{path: "/user/:id",element: <UserPage />,errorElement: <ErrorLayout />, // 捕獲本路由及子路由錯誤loader: async () => {const res = await fetchData();if (res.status === 404) {throw new Response("Not Found", { status: 404 }); // 拋出錯誤}return res.json();}
}// 服務端捕獲錯誤
try {await Promise.all(promises);
} catch (error) {context.errors = error; // 傳遞到 StaticRouterProvider
}
五、createStaticRouter 最佳實踐
- 數據序列化:確保
loader
返回的數據可被序列化為 JSON - 錯誤類型:使用
Response
對象拋出HTTP
錯誤狀態 - 路由復用:服務端/客戶端使用相同的路由配置對象
- 緩存控制:對靜態路由實現
loader
數據緩存
注意:createStaticRouter
僅用于服務端環境,客戶端應使用 createBrowserRouter
或 createMemoryRouter
。