コンテンツにスキップ

Error Handling

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

错误处理

Remix 在 Web 应用程序错误处理方面开创了先例,您一定会喜欢。Remix 会自动捕获代码、服务器或浏览器中的大多数错误,并渲染距离错误发生位置最近的 ErrorBoundary。如果您熟悉 React 的 componentDidCatchgetDerivedStateFromError 类组件钩子,它就是这样的,但对服务器上的错误进行了一些额外的处理。

Remix 将自动捕获错误,并在以下情况下渲染抛出错误的最近错误边界:

  • 在浏览器中渲染
  • 在服务器上渲染
  • 在初始服务器渲染文档请求期间的 loader
  • 在初始服务器渲染文档请求期间的 action
  • 在浏览器客户端转换期间的 loader 中(Remix 序列化错误并通过网络将其发送到浏览器)
  • 在浏览器客户端转换期间的 action

根错误边界

默认情况下,Remix 内置了默认的ErrorBoundary,但我们希望您能为自己的全局错误边界添加一些品牌。您可以通过从app/root.tsx导出自己的 ErrorBoundary 来实现。每当出现未捕获的错误时,您的用户都会看到这些内容。

export function ErrorBoundary() {
const error = useRouteError();
console.error(error);
return (
<html>
<head>
<title>Oh no!</title>
<Meta />
<Links />
</head>
<body>
{/* add the UI you want your users to see */}
<Scripts />
</body>
</html>
);
}

您需要确保仍然渲染 LinksMetaScripts 组件,因为在渲染根错误边界时整个文档将会挂载和卸载。

嵌套错误边界

层次结构中的每个路由都是一个潜在的错误边界。如果嵌套路由导出错误边界,则其下方的任何错误都将被捕获并呈现在那里。这意味着父路由中其余的周围 UI 继续正常呈现,因此用户可以点击另一个链接,而不会丢失他们可能拥有的任何客户端状态。

例如,考虑以下路由:

app/
├── routes/
│ ├── sales.tsx
│ ├── sales.invoices.tsx
│ └── sales.invoices.$invoiceId.tsx
└── root.tsx

如果 app/routes/sales.invoices.$invoiceId.tsx 导出 ErrorBoundary 并且其组件、actionloader 中抛出错误,则应用程序的其余部分将正常呈现,并且只有页面的发票部分会呈现错误。

![嵌套路由出现错误,而父路由的导航正常呈现][嵌套路由出现错误,而父路由的导航正常呈现]

如果路由没有错误边界,错误就会冒泡到最近的错误边界,一直到根,所以您不必向每个路由添加错误边界——只有当您想在 UI 中添加额外的内容时才需要这样做。

错误清理

在生产模式下,服务器上发生的任何错误都会自动清理,以防止将任何敏感的服务器信息(例如堆栈跟踪)泄露给客户端。这意味着您从 useRouteError 收到的 Error 实例将包含通用消息,而没有堆栈跟踪:

export async function loader() {
if (badConditionIsTrue()) {
throw new Error("Oh no! Something went wrong!");
}
}
export function ErrorBoundary() {
const error = useRouteError();
// When NODE_ENV=production:
// error.message = "Unexpected Server Error"
// error.stack = undefined
}

如果你需要记录这些错误或将其报告给第三方服务(如 BugSnagSentry),那么你可以通过 app/entry.server.js 中的 handleError 导出来执行此操作。此方法也会在服务器上运行,因此会收到未清理的错误版本。

如果您想触发错误边界并在浏览器中显示特定消息或数据,那么您可以从action/loader中抛出一个带有该数据的Response

export async function loader() {
if (badConditionIsTrue()) {
throw new Response("Oh no! Something went wrong!", {
status: 500,
});
}
}
export function ErrorBoundary() {
const error = useRouteError();
if (isRouteErrorResponse(error)) {
// error.status = 500
// error.data = "Oh no! Something went wrong!"
}
}