特殊文件
本页内容

特殊文件

React Router 在您的项目中查找一些特殊文件。并非所有这些文件都是必需的。

react-router.config.ts

此文件是可选的。

配置文件用于配置应用程序的某些方面,例如是否使用服务器端渲染、某些目录的位置等等。

import type { Config } from "@react-router/dev/config";

export default {
  // Config options...
} satisfies Config;

有关详细信息,请参阅 react-router 配置 API

root.tsx

此文件是必需的。

"root" 路由 (app/root.tsx) 是 React Router 应用程序中唯一**必需**的路由,因为它是在 routes/ 目录中所有路由的父级,并且负责渲染根 <html> 文档。

由于根路由管理您的文档,因此它是渲染 React Router 提供的一些“文档级”组件的合适位置。这些组件在根路由内仅使用一次,并且包括 React Router 为了正确渲染页面而发现或构建的所有内容。

import type { LinksFunction } from "react-router";
import {
  Links,
  Meta,
  Outlet,
  Scripts,
  ScrollRestoration,
} from "react-router";

import "./global-styles.css";

export default function App() {
  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <meta
          name="viewport"
          content="width=device-width, initial-scale=1"
        />

        {/* All `meta` exports on all routes will render here */}
        <Meta />

        {/* All `link` exports on all routes will render here */}
        <Links />
      </head>
      <body>
        {/* Child routes render here */}
        <Outlet />

        {/* Manages scroll position for client-side transitions */}
        {/* If you use a nonce-based content security policy for scripts, you must provide the `nonce` prop. Otherwise, omit the nonce prop as shown here. */}
        <ScrollRestoration />

        {/* Script tags go here */}
        {/* If you use a nonce-based content security policy for scripts, you must provide the `nonce` prop. Otherwise, omit the nonce prop as shown here. */}
        <Scripts />
      </body>
    </html>
  );
}

Layout 导出

根路由支持所有路由模块导出

根路由还支持一个额外的可选 Layout 导出。Layout 组件有两个作用

  1. 避免在根组件、HydrateFallbackErrorBoundary 中重复文档的“应用外壳”
  2. 防止 React 在根组件/HydrateFallback/ErrorBoundary 之间切换时重新挂载应用外壳元素,这可能会导致 FOUC(闪屏),因为 React 会从 <Links> 组件中移除并重新添加 <link rel="stylesheet"> 标签。
export function Layout({ children }) {
  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <meta
          name="viewport"
          content="width=device-width, initial-scale=1"
        />
        <Meta />
        <Links />
      </head>
      <body>
        {/* children will be the root Component, ErrorBoundary, or HydrateFallback */}
        {children}
        <Scripts />
        <ScrollRestoration />
      </body>
    </html>
  );
}

export default function App() {
  return <Outlet />;
}

export function ErrorBoundary() {}

关于 Layout 组件中的 useLoaderData 的说明

useLoaderData 不允许在 ErrorBoundary 组件中使用,因为它旨在用于正常路径的路由渲染,并且其类型定义有一个内置假设,即 loader 成功运行并返回了一些内容。这个假设在 ErrorBoundary 中不成立,因为它可能是 loader 抛出错误并触发了边界!为了在 ErrorBoundary 中访问加载器数据,您可以使用 useRouteLoaderData,它考虑了加载器数据可能为 undefined 的情况。

由于您的 Layout 组件在成功和错误流中都使用,因此相同的限制也适用。如果您需要根据请求是否成功在 Layout 中分叉逻辑,您可以使用 useRouteLoaderData("root")useRouteError()

由于您的 <Layout> 组件用于渲染 ErrorBoundary,因此您应该非常谨慎地确保您可以渲染 ErrorBoundary 而不遇到任何渲染错误。如果您的 Layout 在尝试渲染边界时抛出另一个错误,则它将无法使用,并且您的 UI 将回退到非常简单的内置默认 ErrorBoundary

export function Layout({
  children,
}: {
  children: React.ReactNode;
}) {
  const data = useRouteLoaderData("root");
  const error = useRouteError();

  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <meta
          name="viewport"
          content="width=device-width, initial-scale=1"
        />
        <Meta />
        <Links />
        <style
          dangerouslySetInnerHTML={{
            __html: `
              :root {
                --themeVar: ${
                  data?.themeVar || defaultThemeVar
                }
              }
            `,
          }}
        />
      </head>
      <body>
        {data ? (
          <Analytics token={data.analyticsToken} />
        ) : null}
        {children}
        <ScrollRestoration />
        <Scripts />
      </body>
    </html>
  );
}

routes.ts

此文件是必需的。

routes.ts 文件用于配置哪些 URL 模式与哪些路由模块匹配。

import {
  type RouteConfig,
  route,
} from "@react-router/dev/routes";

export default [
  route("some/path", "./some/file.tsx"),
  // pattern ^           ^ module file
] satisfies RouteConfig;

有关更多信息,请参阅路由指南

entry.client.tsx

此文件是可选的。

默认情况下,React Router 将为您处理在客户端上进行应用程序的 hydration。您可以使用以下方法显示默认的入口客户端文件

react-router reveal

此文件是浏览器的入口点,负责将服务器在您的服务器入口模块中生成的标记进行 hydration,但是您也可以在此处初始化任何其他客户端代码。

import { startTransition, StrictMode } from "react";
import { hydrateRoot } from "react-dom/client";
import { HydratedRouter } from "react-router/dom";

startTransition(() => {
  hydrateRoot(
    document,
    <StrictMode>
      <HydratedRouter />
    </StrictMode>
  );
});

这是浏览器中运行的第一段代码。您可以初始化客户端库,添加仅客户端的提供程序等。

entry.server.tsx

此文件是可选的。

默认情况下,React Router 将为您处理生成 HTTP 响应。您可以使用以下方法显示默认的服务器入口文件

react-router reveal

此模块的 default 导出是一个函数,允许您创建响应,包括 HTTP 状态、标头和 HTML,让您可以完全控制标记的生成和发送到客户端的方式。

此模块应使用带有当前请求的 contexturl<ServerRouter> 元素渲染当前页面的标记。一旦 JavaScript 在浏览器中加载,此标记将(可选地)使用客户端入口模块重新 hydration。

handleDataRequest

您可以导出一个可选的 handleDataRequest 函数,它允许您修改数据请求的响应。这些是不会渲染 HTML 的请求,而是将加载器和操作数据在客户端 hydration 发生后返回到浏览器。

export function handleDataRequest(
  response: Response,
  {
    request,
    params,
    context,
  }: LoaderFunctionArgs | ActionFunctionArgs
) {
  response.headers.set("X-Custom-Header", "value");
  return response;
}

handleError

默认情况下,React Router 会将遇到的服务器端错误记录到控制台。如果您希望更好地控制日志记录,或者希望将这些错误报告给外部服务,则可以导出一个可选的 handleError 函数,它将为您提供控制权(并将禁用内置错误日志记录)。

export function handleError(
  error: unknown,
  {
    request,
    params,
    context,
  }: LoaderFunctionArgs | ActionFunctionArgs
) {
  if (!request.signal.aborted) {
    sendErrorToErrorReportingService(error);
    console.error(formatErrorForJsonLogging(error));
  }
}

请注意,通常您希望避免在请求被中止时记录日志,因为 React Router 的取消和竞争条件处理会导致许多请求被中止。

流式渲染错误

当您通过renderToPipeableStreamrenderToReadableStream流式传输 HTML 响应时,您自己的 handleError 实现将只处理在初始 shell 渲染期间遇到的错误。如果您在后续流式渲染期间遇到渲染错误,则需要手动处理这些错误,因为 React Router 服务器此时已经发送了 Response。

对于 renderToPipeableStream,您可以在 onError 回调函数中处理这些错误。您需要在 onShellReady 中切换一个布尔值,以便知道错误是 shell 渲染错误(可以忽略)还是异步错误

例如,请参阅 Node 的默认entry.server.tsx

抛出的响应

请注意,这不会处理来自 loader/action 函数的抛出的 Response 实例。此处理程序的目的是查找代码中导致意外抛出错误的 bug。如果您正在检测某个场景并在 loader/action 中抛出一个 401/404/等 Response,那么这是由您的代码处理的预期流程。如果您还希望记录这些错误或将其发送到外部服务,则应在抛出响应时进行。

文档和示例 CC 4.0