コンテンツにスキップ

root

このコンテンツはまだ日本語訳がありません。

根路由

路由(app/root.tsx)是 Remix 应用程序中唯一必需的路由,因为它是routes/目录中所有路由的父级,并负责呈现根<html>文档。

除此之外,它基本上就像任何其他路由一样,并支持所有标准路由导出:

由于根路由管理您的文档,因此它是呈现 Remix 提供的少数文档级组件的合适位置。这些组件将在您的根路由中使用一次,它们包括 Remix 为正确呈现您的页面而计算或构建的所有内容。

app/root.tsx
import type { LinksFunction } from "@remix-run/node"; // or cloudflare/deno
import {
Links,
LiveReload,
Meta,
Outlet,
Scripts,
ScrollRestoration,
} from "@remix-run/react";
import globalStylesheetUrl from "./global-styles.css";
export const links: LinksFunction = () => {
return [{ rel: "stylesheet", href: globalStylesheetUrl }];
};
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 />
{/* Sets up automatic reload when you change code */}
{/* and only does anything during development */}
{/* 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. */}
<LiveReload />
</body>
</html>
);
}

布局导出

由于根路由管理所有路由的文档,因此它还支持额外的可选布局导出。您可以在此 RFC 中阅读详细信息,但布局路由有两个用途:

  • 避免在根组件、HydrateFallbackErrorBoundary 中重复文档/应用外壳
  • 避免在根组件/HydrateFallback/ErrorBoundary 之间切换时 React 重新挂载应用外壳元素,如果 React 从<Links> 组件中删除并重新添加<link rel="stylesheet"> 标签,则可能导致 FOUC。
app/root.tsx
import {
Links,
LiveReload,
Meta,
Outlet,
Scripts,
ScrollRestoration,
} from "@remix-run/react";
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 />
<LiveReload />
</body>
</html>
);
}
export default function App() {
return <Outlet />;
}
export function ErrorBoundary() {
const error = useRouteError();
if (isRouteErrorResponse(error)) {
return (
<>
<h1>
{error.status} {error.statusText}
</h1>
<p>{error.data}</p>
</>
);
}
return (
<>
<h1>Error!</h1>
<p>{error?.message ?? "Unknown error"}</p>
</>
);
}

关于 Layout 组件中的 useLoaderData 的说明

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

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

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

// "app/root.tsx"
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>
);
}

参见: