「根」路由 (app/root.tsx
) 是您的 Remix 應用程式中唯一必要的路由,因為它是您 routes/
目錄中所有路由的父級,並且負責渲染根 <html>
文件。
除此之外,它基本上與任何其他路由相同,並支援所有標準路由導出
headers
meta
links
loader
clientLoader
action
clientAction
default
ErrorBoundary
HydrateFallback
handle
shouldRevalidate
由於根路由管理您的文件,因此它是渲染 Remix 提供的一些「文件層級」元件的適當位置。這些元件應在您的根路由內使用一次,它們包含 Remix 為使您的頁面正常渲染而計算或建構的所有內容。
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>
);
}
由於根路由管理所有路由的文件,它也支援額外的可選 Layout
導出。您可以在此 RFC 中閱讀詳細資訊,但版面配置路由有兩個用途
HydrateFallback
和 ErrorBoundary
中重複您的文件/「應用程式外殼」HydrateFallback
/ErrorBoundary
之間切換時,React 重新掛載您的應用程式外殼元素,如果 React 從您的 <Links>
元件中移除並重新添加 <link rel="stylesheet">
標籤,可能會導致 FOUC。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
的注意事項
useLoaderData
不允許在 ErrorBoundary
元件中使用,因為它是用於正常路徑路由渲染的,並且其類型定義內建了 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>
);
}
另請參閱