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!"
}
}