React第六十二节 Router中 createStaticRouter 的使用详解

前言

createStaticRouter 是 React Router 专为 服务端渲染(SSR) 设计的 API ,用于在服务器端处理路由匹配和数据加载。它在构建静态 HTML 响应时替代了客户端的 BrowserRouter,确保 SSR 和客户端 Hydration 的路由状态一致。

一、createStaticRouter 核心用途

  1. 服务端路由匹配 :根据请求 URL 确定渲染的组件
  2. 数据预加载 :执行路由的 loader 函数获取初始数据
  3. 错误处理:捕获渲染过程中的路由级错误
  4. 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关键配置说明

参数 作用

  1. basename: 应用基础路径 (e.g. /app)
  2. location: 当前请求 URL 对象 (必需)
  3. router.matches: 当前 URL 匹配的路由对象数组
  4. 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 最佳实践

  1. 数据序列化 :确保 loader 返回的数据可被序列化为 JSON
  2. 错误类型 :使用 Response 对象抛出 HTTP 错误状态
  3. 路由复用:服务端/客户端使用相同的路由配置对象
  4. 缓存控制 :对静态路由实现 loader 数据缓存

注意 :createStaticRouter 仅用于服务端环境,客户端应使用 createBrowserRouter 或 createMemoryRouter。

相关推荐
郑州光合科技余经理6 小时前
海外版外卖加盟:总站与分站配送规则怎么分开管
java·开发语言·前端·后端·uni-app·php·ai编程
小呆呆6666 小时前
副业搞起来,小说,漫画,漫剧的成本优化思路
前端·后端·面试
凤城老人7 小时前
从 PyQt6 到 Electron:给 Edge TTS 做一个“多角色配音机“的踩坑手记
javascript·typescript·electron
Dovis(誓平步青云)7 小时前
浇水提醒刚弹出又消失,植物状态别只存一个百分比
开发语言·前端·javascript·pdf·ecmascript·电脑
码艺-Alimjan7 小时前
Vben Admin 新增维吾尔语 Vben-Modal的关键坑之一
前端·javascript·vue.js
可乐鸡翅yeah_8 小时前
hls.js 手动自定义 http 请求 loader,修改请求头实战
开发语言·前端·javascript·网络协议·http·ecmascript·m3u8在线
IT_陈寒8 小时前
Vite静态资源导入这个坑我帮你们踩过了
前端·人工智能·后端
广州华水科技8 小时前
大坝安全监测解决方案:单北斗GNSS形变监测系统应用与维护
前端
yivifu9 小时前
中文古籍电子书注释集成
前端·javascript·python·beautifulsoup·epub
weixin_4482902510 小时前
书庐开发实战教学
javascript·css·html