前言
createStaticRouter
是 React Router
专为 服务端渲染(SSR) 设计的 API ,用于在服务器端处理路由匹配
和数据加载
。它在构建静态 HTML
响应时替代了客户端的 BrowserRouter
,确保 SSR
和客户端 Hydration
的路由状态一致。
一、createStaticRouter 核心用途
- 服务端路由匹配 :根据请求
URL
确定渲染的组件 - 数据预加载 :执行路由的
loader
函数获取初始数据 - 错误处理:捕获渲染过程中的路由级错误
SSR/SSG 支持
:生成包含初始数据的静态 HTML
二、createStaticRouter 使用步骤详解(配合 Express
示例)
2.1、 定义路由配置
javascript
// 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、 服务端路由处理
javascript
// 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. 触发所有匹配路由的 loader
const 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
javascript
// 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 错误处理机制
javascript
// 在路由配置中添加错误边界
{
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
。