root.tsx

root.tsx

摘要

此文件为必需项

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

import { Outlet, Scripts } from "react-router";

import "./global-styles.css";

export default function App() {
  return (
    <html lang="en">
      <head>
        <link rel="icon" href="/favicon.ico" />
      </head>
      <body>
        <Outlet />
        <Scripts />
      </body>
    </html>
  );
}

要渲染的组件

因为根路由管理着您的文档,所以它是渲染 React Router 提供的一些“文档级”组件的正确位置。这些组件将在您的根路由中使用一次,它们包含了 React Router 为使您的页面正确渲染而计算或构建的所有内容。

import {
  Outlet,
  Scripts,
  ScrollRestoration,
} from "react-router";

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

如果您未使用 React 19,或者选择不使用 React 的 <link><title><meta> 组件,而是依赖于 React Router 的 linksmeta 导出,您需要将以下内容添加到您的根路由中。

import { Links, Meta } from "react-router";

export default function App() {
  return (
    <html lang="en">
      <head>
        {/* All `meta` exports on all routes will render here */}
        <Meta />

        {/* All `link` exports on all routes will render here */}
        <Links />
      </head>
      <body>
        <Outlet />
        <ScrollRestoration />
        <Scripts />
      </body>
    </html>
  );
}

布局导出

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

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

  1. 避免在根组件、HydrateFallbackErrorBoundary 之间重复文档的“应用外壳”
  2. 防止 React 在根组件/HydrateFallback/ErrorBoundary 之间切换时重新挂载您的应用外壳元素,如果 React 从 <Links> 组件中移除并重新添加 <link rel="stylesheet"> 标签,这可能导致无样式内容闪烁(FOUC)。

Layout 接收一个 children 属性,它是 default 导出(例如 App)、HydrateFallbackErrorBoundary

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 的说明

ErrorBoundary 组件中不允许使用 useLoaderData,因为它旨在用于正常路径的路由渲染,并且其类型定义内置了一个假设,即 loader 成功运行并返回了数据。这个假设在 ErrorBoundary 中不成立,因为可能是 loader 抛出错误并触发了错误边界!为了在 ErrorBoundary 中访问 loader 数据,您可以使用 useRouteLoaderData,它考虑了 loader 数据可能为 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>
  );
}
文档和示例 CC 4.0
编辑