错误处理

错误处理

¥Error Handling

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

¥Remix sets a new precedent in web application error handling that you are going to love. Remix automatically catches most errors in your code, on the server or in the browser, and renders the closest ErrorBoundary to where the error occurred. If you're familiar with React's componentDidCatch and getDerivedStateFromError class component hooks, it's just like that but with some extra handling for errors on the server.

Remix 将自动捕获错误并渲染抛出错误的最近错误边界,同时:

¥Remix will automatically catch errors and render the nearest error boundary for errors thrown while:

  • 在浏览器中渲染

    ¥rendering in the browser

  • 在服务器上渲染

    ¥rendering on the server

  • 在初始服务器渲染文档请求期间的 loader

    ¥in a loader during the initial server-rendered document request

  • 在初始服务器渲染文档请求期间的 action

    ¥in an action during the initial server-rendered document request

  • 在浏览器客户端转换期间的 loader 中(Remix 会序列化错误并通过网络将其发送到浏览器)

    ¥in a loader during a client-side transition in the browser (Remix serializes the error and sends it over the network to the browser)

  • 在浏览器客户端转换期间的 action

    ¥in an action during a client-side transition in the browser

根错误边界

¥Root Error Boundary

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

¥By default, Remix ships with a default ErrorBoundary built-in, but we hope you'll want to add a little branding to your own global error boundary. You can do so by exporting your own ErrorBoundary from app/root.tsx. This is what your users will see whenever an uncaught error is thrown.

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 组件,因为在渲染根错误边界时,整个文档将会挂载和卸载。

¥You'll want to make sure to still render the Links, Meta, and Scripts components because the whole document will mount and unmount when the root error boundary is rendered.

嵌套错误边界

¥Nested Error Boundaries

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

¥Each route in the hierarchy is a potential error boundary. If a nested route exports an error boundary, then any errors below it will be caught and rendered there. This means that the rest of the surrounding UI in the parent routes continue to render normally so the user is able to click another link and not lose any client-side state they might have had.

例如,考虑这些路由:

¥For example, consider these routes:

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

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

¥If app/routes/sales.invoices.$invoiceId.tsx exports an ErrorBoundary and an error is thrown in its component, action, or loader, the rest of the app renders normally and only the invoice section of the page renders the error.

error in a nested route where the parent route's navigation renders normally

如果路由没有错误边界,错误 "冒泡" 会一直指向最近的错误边界,直到根节点。因此,你无需为每个路由添加错误边界 - 只有当你想要为 UI 添加额外功能时才需要添加。

¥If a route doesn't have an error boundary, the error "bubbles up" to the closest error boundary, all the way to the root, so you don't have to add error boundaries to every route--only when you want to add that extra touch to your UI.

错误清理

¥Error Sanitization

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

¥In production mode, any errors that happen on the server are automatically sanitized to prevent leaking any sensitive server information (such as stack traces) to the client. This means that the Error instance you receive from useRouteError will have a generic message and no stack trace:

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 导出功能来实现。由于此方法也在服务器上运行,因此它会接收未净化的错误版本。

¥If you need to log these errors or report them to a third-party service such as BugSnag or Sentry, then you can do this through a handleError export in your app/entry.server.js. This method receives the unsanitized versions of the error since it is also running on the server.

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

¥If you want to trigger an error boundary and display a specific message or data in the browser, then you can throw a Response from a action/loader with that data instead:

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!"
  }
}
Remix v2.17 中文网 - 粤ICP备13048890号