React Router 错误边界(Error Boundaries)完整指南:Framework 与 Data 模式下的路由级错误捕获

React Router 错误边界(Error Boundaries)完整指南:Framework 与 Data 模式下的路由级错误捕获 React Router 错误边界Error Boundaries完整指南Framework 与 Data 模式下的路由级错误捕获【免费下载链接】react-routerDeclarative routing for React项目地址: https://gitcode.com/GitHub_Trending/re/react-router导读本文基于 React Routerv7位于本仓库react-router项目官方文档与源码系统讲解路由模块自动捕获运行时错误并渲染最近ErrorBoundary的机制。你将掌握在Framework Modeexport function ErrorBoundary props与Data ModecreateBrowserRouter对象路由 useRouteError两种模式下搭建根级与嵌套错误边界的完整方案、如何通过throw data()表达 404 等预期错误以及生产环境下服务端错误的清洗Sanitization规则最终做到绝不向用户渲染空白页面。路由模块会自动捕获你代码中的错误并把错误交给最近的ErrorBoundary渲染从而避免用户看到空白页面。但请注意错误边界不是为渲染表单校验错误或做错误上报设计的这两类场景请分别参考 Form Validation 与 Error Reporting。1. 添加根级错误边界三种错误的统一出口所有应用都应至少导出一个根级错误边界它需要覆盖三种主要错误来源通过throw data()抛出、带状态码与文本的响应型错误ErrorResponse带堆栈追踪的Error实例及其子类如ReferenceError被随意 throw 的任意值非响应、非 Error 的未知值。在 React Router 的 DOM 运行时中isRouteErrorResponse是一个类型守卫函数见 router/utils.ts可用来判断当前错误是否为带status/statusText/data的响应错误。Framework Mode 下的根错误边界适用前提项目通过 Framework Mode 运行例如使用 Vite 插件与routes.ts约定。在 Framework Mode 下错误会作为props直接传入路由级错误边界类型为Route.ErrorBoundaryProps因此无需任何 hook就能拿到错误对象。类型定义位于 route-module-annotations.tsErrorBoundaryProps至少包含params路由动态参数与error: unknown两个字段当启用 RSC 时还会带上loaderData与actionData。在根模块通常是app/root.tsx中import { Route } from ./types/root; export function ErrorBoundary({ error, }: Route.ErrorBoundaryProps) { if (isRouteErrorResponse(error)) { return ( h1 {error.status} {error.statusText} /h1 p{error.data}/p / ); } else if (error instanceof Error) { return ( div h1Error/h1 p{error.message}/p pThe stack trace is:/p pre{error.stack}/pre /div ); } else { return h1Unknown Error/h1; } }注意上述Route.ErrorBoundaryProps中的error类型是unknown这正是任何东西都可能被 throw的体现——因此else分支的兜底渲染是必要的。Data Mode 下的根错误边界适用前提项目以 Data Mode 运行即手动使用createBrowserRouter等数据路由 API 而非 Framework 路由约定。在 Data Mode 下ErrorBoundary不接收 props你需要通过useRouteErrorhook 获取当前错误。useRouteError的实现见 hooks.tsx它优先从RouteErrorContext渲染期错误取值否则从数据路由状态state.errors?.[routeId]loader/action 期错误读取因此同一套 UI 逻辑可以覆盖两类来源import { useRouteError } from react-router; let router createBrowserRouter([ { path: /, ErrorBoundary: RootErrorBoundary, Component: Root, }, ]); function Root() { /* ... */ } function RootErrorBoundary() { let error useRouteError(); if (isRouteErrorResponse(error)) { return ( h1 {error.status} {error.statusText} /h1 p{error.data}/p / ); } else if (error instanceof Error) { return ( div h1Error/h1 p{error.message}/p pThe stack trace is:/p pre{error.stack}/pre /div ); } else { return h1Unknown Error/h1; } }可以看到除了获取错误的方式不同props vs hook两种模式的分支判断与 UI 输出可以完全一致这也是文档鼓励至少写一个根错误边界覆盖三类错误的原因。2. 制造一个 bug 来验证捕获效果错误边界的主要职责是捕获代码中非预期的错误官方并不推荐故意 throw 错误来驱动控制流。在任意一个路由模块中写一个必然失败的 loaderexport async function loader() { return undefined(); }调用undefined会抛出TypeError属于Error实例此时 UI 会渲染第 1 节代码中的error instanceof Error分支展示error.message与堆栈。这种机制并不仅限于 loader而是覆盖全部路由模块 APIloaders、actions、components、headers、links 和 meta。从底层实现看见 server-runtime/errors.ts 顶部注释React Router 是为了弥补 React 原生componentDidCatch无法覆盖 SSR 渲染与数据加载阶段的不足渲染抛错走正常 React 错误捕获冒泡而 loader/action 抛错则由路由框架模拟最近的错误边界完成重新渲染。仓库中相应的集成测试可验证数据加载抛错与渲染抛错分别落到正确的边界上参见 integration/error-boundary-test.ts 与 integration/error-boundary-v2-test.ts。3. 在 loader/action 中抛出带状态码的响应错误规则 #2 存在例外尤其是404。当 loader 找不到渲染页面所需的数据时你可以主动throw data(...)携带正确的状态码把错误抛给最近的错误边界——抛一个 404然后继续前进。import { data } from react-router; export async function loader({ params }) { let record await fakeDb.getRecord(params.id); if (!record) { throw data(Record Not Found, { status: 404 }); } return record; }此时渲染的是第 1 节代码中的isRouteErrorResponse分支error.status为404、error.statusText为Record Not Found、error.data为传给data()的载荷。相比直接渲染空白页用户会看到明确的未找到反馈相比任意抛错这属于预期中的错误适合用响应式错误来表达。4. 嵌套错误边界最近错误边界的解析规则当某层路由抛错时React Router 会渲染离错误发生处最近的、向上查找第一个配置了ErrorBoundary的路由。也就是说错误会向父级逐层冒泡直到遇到一个有边界的祖先。Framework Mode 的嵌套示例考虑如下嵌套路由app.tsx与invoice-page.tsx各自导出错误边界invoices.tsx与payments.tsx没有// ✅ has error boundary route(/app, app.tsx, [ // ❌ no error boundary route(invoices, invoices.tsx, [ // ✅ has error boundary route(invoices/:id, invoice-page.tsx, [ // ❌ no error boundary route(payments, payments.tsx), ]), ]), ]);给定错误来源实际渲染的边界如下error originrendered boundaryapp.tsxapp.tsxinvoices.tsxapp.tsxinvoice-page.tsxinvoice-page.tsxpayments.tsxinvoice-page.tsx解读invoices.tsx自己没有边界向上冒泡到app.tsx被捕获payments.tsx无边界向上依次是invoice-page.tsx有边界因此由invoice-page.tsx接管。各层路由模块导出边界的方式参考 route-module。Data Mode 的嵌套示例Data Mode 下等价的路由树在对象配置中以ErrorBoundary字段声明let router createBrowserRouter([ { path: /app, Component: App, ErrorBoundary: AppErrorBoundary, // ✅ has error boundary children: [ { path: invoices, Component: Invoices, // ❌ no error boundary children: [ { path: :id, Component: Invoice, ErrorBoundary: InvoiceErrorBoundary, // ✅ has error boundary children: [ { path: payments, Component: Payments, // ❌ no error boundary }, ], }, ], }, ], }, ]);渲染规则与 Framework Mode 完全一致error originrendered boundaryAppAppErrorBoundaryInvoicesAppErrorBoundaryInvoiceInvoiceErrorBoundaryPaymentsInvoiceErrorBoundary补充一个框架层面的边界细节文档与源码server-runtime/errors.ts 头注释都指出——当action 抛错后需要渲染该 action 的错误边界时如果渲染过程中父路由的 loader 又抛了新错误框架会忽略这个后来者始终优先呈现 action 的原始错误因为先发生的错误不应被后到者插队且它通常能渲染得最深。5. 生产环境下的错误清洗Error Sanitization适用前提Framework Mode。在 Framework Mode 中当以生产模式构建即非开发模式时发生在服务器上的任何错误在发往浏览器之前都会被自动清洗以防止泄漏服务器敏感信息例如堆栈追踪。其实现位于 server-runtime/errors.tssanitizeError会把ServerMode.Development之外的任何Error替换为固定消息Unexpected Server Error并将stack置为undefined。对应的集成测试见 integration/error-sanitization-test.ts。由此产生两个直接结论在生产环境的浏览器端一个被抛出的Error会表现为通用消息Unexpected Server Error且没有堆栈而服务器端的原始错误对象不会被改动开发者仍可在服务端日志中看到完整堆栈。通过throw data(yourData)携带的data 载荷不会被清洗——因为这些数据本就是设计出来渲染给用户的例如友好的错误文案不属于敏感信息。因此生产环境下isRouteErrorResponse分支中error.data的内容会原样到达客户端。开发模式不受影响需要强调清洗仅发生在非开发模式开发模式下错误保留真实消息与堆栈serializeErrors甚至会记录错误子类型__subType用于在客户端水合时重建同类型的错误实例参见 server-runtime/errors.ts。因此生产环境看不到堆栈不应被误解为开发调试受限——开发模式天然保留完整诊断信息。6. 综合建议将本文要点落地到真实项目时可以遵循以下检查清单根边界必须有每个应用至少实现一个覆盖isRouteErrorResponse/Error实例 / 未知值三种分支的根级ErrorBoundaryFramework 用Route.ErrorBoundaryPropsData 用useRouteError保证任何情况下用户都看到有意义的界面而不是白屏404 用响应错误loader/action 找不到资源时用throw data(..., { status: 404 })表达交给isRouteErrorResponse分支渲染纵深防御在关键业务路由上配置自己的ErrorBoundary让局部错误就地呈现避免直接暴露全局页或丢失父级布局上下文同时记住渲染边界遵循最近向上查找规则上报与校验分离错误边界捕获到的错误如需上报请参考 Error Reporting表单校验结果应走返回 data 的正常通道参考 Form Validation不要用抛错模拟控制流类型推断关于 Framework Mode 下Route.ErrorBoundaryProps如何由loader/action推导loaderData等类型细节可进一步阅读 type-safety。通过根级兜底 分层错误边界 data()表达预期错误 生产错误清洗这套组合应用能够在数据加载、提交动作与渲染任意阶段发生故障时都给出可靠、安全且信息恰当的用户反馈。【免费下载链接】react-routerDeclarative routing for React项目地址: https://gitcode.com/GitHub_Trending/re/react-router创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考