Remix 在 Web 应用错误处理方面树立了新的标杆,你一定会喜欢它。Remix 会自动捕获代码中发生的大多数错误(无论是服务器端还是浏览器端),并渲染最接近于错误发生位置的 ErrorBoundary
。如果你熟悉 React 的 componentDidCatch
和 getDerivedStateFromError
类组件钩子,那么这就像它们一样,只是在服务器端错误处理方面有一些额外处理。
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>
);
}
你应该确保仍然渲染 Links
、Meta
和 Scripts
组件,因为当根错误边界渲染时,整个文档将会挂载和卸载。
层次结构中的每个路由都是一个潜在的错误边界。如果嵌套路由导出了一个错误边界,那么它下面的任何错误都将在那里被捕获并渲染。这意味着父路由中的其余 UI 将继续正常渲染,因此用户可以点击另一个链接,并且不会丢失他们可能拥有的任何客户端状态。
例如,考虑以下路由
app/
├── routes/
│ ├── sales.tsx
│ ├── sales.invoices.tsx
│ └── sales.invoices.$invoiceId.tsx
└── root.tsx
如果 app/routes/sales.invoices.$invoiceId.tsx
导出了一个 ErrorBoundary
,并且它的组件、action
或 loader
中抛出了错误,则应用程序的其余部分将正常渲染,只有页面的发票部分会渲染错误。
如果路由没有错误边界,则错误将“冒泡”到最近的错误边界,一直到根边界,因此你无需在每个路由中添加错误边界,只有在需要在 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
}
如果你需要记录这些错误或将它们报告给第三方服务,例如 BugSnag 或 Sentry,那么你可以在 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!"
}
}