自定义框架
本页内容

自定义框架

简介

您可以使用数据模式将 React Router 的框架功能(如加载器、操作、获取器等)集成到您自己的捆绑器和服务器抽象中,而无需使用 @react-router/dev

客户端渲染

1. 创建一个路由器

启用路由模块 API(加载器、操作等)的浏览器运行时 API 是 createBrowserRouter

它接受一个路由对象数组,这些对象支持加载器、操作、错误边界等。React Router Vite 插件会从 routes.ts 创建一个这样的数组,但您也可以手动(或通过抽象)创建一个,并使用您自己的捆绑器。

import { createBrowserRouter } from "react-router";

let router = createBrowserRouter([
  {
    path: "/",
    Component: Root,
    children: [
      {
        path: "shows/:showId",
        Component: Show,
        loader: ({ request, params }) =>
          fetch(`/api/show/${params.showId}.json`, {
            signal: request.signal,
          }),
      },
    ],
  },
]);

2. 渲染路由器

要在浏览器中渲染路由器,请使用 <RouterProvider>

import {
  createBrowserRouter,
  RouterProvider,
} from "react-router";
import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")).render(
  <RouterProvider router={router} />,
);

3. 懒加载

路由可以通过 lazy 属性懒加载其大部分定义。

createBrowserRouter([
  {
    path: "/show/:showId",
    lazy: {
      loader: async () =>
        (await import("./show.loader.js")).loader,
      action: async () =>
        (await import("./show.action.js")).action,
      Component: async () =>
        (await import("./show.component.js")).Component,
    },
  },
]);

服务器端渲染

要对自定义设置进行服务器端渲染,可以使用一些服务器 API 来进行渲染和数据加载。

本指南仅为您提供一些关于其工作原理的思路。要深入了解,请参阅自定义框架示例仓库

1. 定义你的路由

路由在服务器端和客户端都是同类型的对象。

export default [
  {
    path: "/",
    Component: Root,
    children: [
      {
        path: "shows/:showId",
        Component: Show,
        loader: ({ params }) => {
          return db.loadShow(params.id);
        },
      },
    ],
  },
];

2. 创建一个静态处理器

使用 createStaticHandler 将你的路由转换为请求处理器。

import { createStaticHandler } from "react-router";
import routes from "./some-routes";

let { query, dataRoutes } = createStaticHandler(routes);

3. 获取路由上下文并渲染

React Router 使用 Web Fetch Request,所以如果您的服务器不支持,您需要将它使用的任何对象适配为 Web Fetch Request 对象。

此步骤假定您的服务器接收 Request 对象。

import { renderToString } from "react-dom/server";
import {
  createStaticHandler,
  createStaticRouter,
  StaticRouterProvider,
} from "react-router";

import routes from "./some-routes.js";

let { query, dataRoutes } = createStaticHandler(routes);

export async function handler(request: Request) {
  // 1. run actions/loaders to get the routing context with `query`
  let context = await query(request);

  // If `query` returns a Response, send it raw (a route probably a redirected)
  if (context instanceof Response) {
    return context;
  }

  // 2. Create a static router for SSR
  let router = createStaticRouter(dataRoutes, context);

  // 3. Render everything with StaticRouterProvider
  let html = renderToString(
    <StaticRouterProvider
      router={router}
      context={context}
    />,
  );

  // Setup headers from action and loaders from deepest match
  let leaf = context.matches[context.matches.length - 1];
  let actionHeaders = context.actionHeaders[leaf.route.id];
  let loaderHeaders = context.loaderHeaders[leaf.route.id];
  let headers = new Headers(actionHeaders);
  if (loaderHeaders) {
    for (let [key, value] of loaderHeaders.entries()) {
      headers.append(key, value);
    }
  }

  headers.set("Content-Type", "text/html; charset=utf-8");

  // 4. send a response
  return new Response(`<!DOCTYPE html>${html}`, {
    status: context.statusCode,
    headers,
  });
}

4. 在浏览器中进行 Hydrate

Hydration 数据被嵌入到 window.__staticRouterHydrationData 中,用它来初始化您的客户端路由器并渲染一个 <RouterProvider>

import { StrictMode } from "react";
import { hydrateRoot } from "react-dom/client";
import { RouterProvider } from "react-router/dom";
import routes from "./app/routes.js";
import { createBrowserRouter } from "react-router";

let router = createBrowserRouter(routes, {
  hydrationData: window.__staticRouterHydrationData,
});

hydrateRoot(
  document,
  <StrictMode>
    <RouterProvider router={router} />
  </StrictMode>,
);
文档和示例 CC 4.0
编辑