尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

TanStack Router CatchBoundary 详解:跨 React、Vue、Solid 的错误捕获、重置与默认错误组件

TanStack Router CatchBoundary 详解:跨 React、Vue、Solid 的错误捕获、重置与默认错误组件 TanStack Router CatchBoundary 详解跨 React、Vue、Solid 的错误捕获、重置与默认错误组件【免费下载链接】router A client-first, server-capable, fully type-safe router and full-stack framework for the web (React and more).项目地址: https://gitcode.com/GitHub_Trending/ro/routerCatchBoundary是 TanStack Router 提供的错误边界组件它捕获子组件抛出的错误渲染指定的错误组件并支持通过getResetKey以声明式方式重置错误状态。阅读本文你将掌握CatchBoundary的全部 props、返回值行为、在 React/Vue/Solid 三端框架下的底层实现差异以及它在路由器内部全局错误边界、每条路由的errorComponent边界中的实际调用位置。CatchBoundary 是什么CatchBoundary组件捕获其 children 抛出的错误渲染一个错误组件并可选地调用onCatch回调。它同时接受一个getResetKey函数可以在 key 变化时声明式地重置组件状态例如用户点击重试按钮后或路由匹配发生变化时自动恢复。三个框架包tanstack/react-router、tanstack/vue-router、tanstack/solid-router均导出CatchBoundary与ErrorComponent例如在 React 包中由 index.tsx 统一导出export { CatchBoundary, ErrorComponent } from ./CatchBoundaryCatchBoundary props 说明CatchBoundary组件接受以下 props以 官方文档 定义为准props.getResetKeyprop类型文档标注为() string从源码签名看三端实际放宽为() unknown见 react-router/CatchBoundary.tsx因此可以返回任意可比较的引用或值必填一个返回字符串或任意值的函数当返回的 key 发生变化时组件的错误状态会被重置props.childrenprop类型React 为React.ReactNodeVue 为Vue.VNodeSolid 为Solid.JSX.Element并额外支持ParentProps必填无错误时渲染的子组件props.errorComponentprop类型React.ReactNode/ 对应框架的组件类型可选默认值ErrorComponent出现错误时渲染的组件。该组件会收到error抛出的值与reset程序化重置错误状态的函数两个 propsprops.onCatchprop类型React(error: unknown, errorInfo: ErrorInfo) voidVue(error: unknown) voidSolid(error: Error) void可选子组件抛出错误时被调用的回调接收抛出的错误CatchBoundary 返回值无错误时返回组件的 children有错误时返回errorComponent未提供时为内置的默认ErrorComponent基本用法示例文档给出的最小示例如下React 端import { CatchBoundary } from tanstack/react-router function Component() { return ( CatchBoundary getResetKey{() reset} onCatch{(error) console.error(error)} divMy Component/div /CatchBoundary ) }getResetKey返回的值每次渲染都会重新求值。当它返回的 key 与上次不同、且组件正处于错误状态时边界会自动重置并重新渲染 children。实践中通常把 key 绑定到可变化的状态上例如路由 match、导航计数或一个手动递增的计数器function RetryDemo() { const [resetKey, setResetKey] React.useState(0) return ( CatchBoundary getResetKey{() resetKey} errorComponent{({ reset }) ( button onClick{() { reset() // 立即重置也可改为 setResetKey(k k 1) }} 重试 /button )} DangerousChild / /CatchBoundary ) }单元测试 errorComponent.test.tsx 验证了这一行为CatchBoundary resets after throwing %s覆盖多种抛出值的类型以及CatchBoundary tracks reset keys while healthy健康状态下也会持续跟踪 reset key 的引用变化。三端实现对比同一个 API三种错误边界机制三个框架包各自基于框架原生的错误边界能力实现了CatchBoundary核心逻辑错误状态、onCatch回调、reset key 监听一一对应但机制不同React类组件 getDerivedStateFromError实现位于 react-router/src/CatchBoundary.tsx。关键设计有三点错误状态用数组包装state.error的类型是[unknown] | 0。源码注释解释了原因Wrapping caught values keeps every possible thrown value truthy——即使用[error]数组包装后任何抛出值包括字符串、null等假值都能被if (state.error)正确判定为处于错误状态而0表示无错误。声明式重置getDerivedStateFromProps在每次 props/状态更新时比较getResetKey()的返回值若 key 变化且当前有错误则返回{ resetKey, error: 0 }清空错误见 源码。onCatch经由componentDidCatch转发componentDidCatch(error: unknown, errorInfo: ErrorInfo) { this.props.onCatch?.(error, errorInfo) }渲染错误分支时errorComponent被显式创建并传入error与reset两个 props在非生产环境下错误组件还会被包进nonRouteComponentContext用于让路由组件类型守卫区分路由内组件与错误/非路由组件源码。VueonErrorCapturedwatch监听 reset key实现位于 vue-router/src/CatchBoundary.tsx。它内部定义了一个VueErrorBoundary组件错误状态是一个shallowRef[unknown] | 0(0)与 React 端同样的0 表示无错误、数组包装错误设计通过Vue.watch(() props.resetKey, ...)监听 key 变化来重置错误源码通过Vue.onErrorCaptured捕获子树错误并调用props.onError同时特意忽略 Promise 或 thenable——这类值通常属于未处理的异步拒绝不应被当作渲染错误处理源码。对外的CatchBoundary只是一个h()薄封装把 API 层参数getResetKey()、onCatch翻译成内部组件的 propsexport function CatchBoundary(props: CatchBoundaryProps) { return Vue.h(VueErrorBoundary, { resetKey: props.getResetKey() as any, onError: props.onCatch, children: props.children, errorComponent: props.errorComponent, }) }Solid封装solid-js原生ErrorBoundary实现位于 solid-router/src/CatchBoundary.tsx。Solid 版本直接复用solid-js/web提供的Solid.ErrorBoundary在其fallback回调中做三件事调用props.onCatch?.(error)建立响应式 effect在getResetKey的值变化时自动调用 Solid 原生的reset使用Solid.createEffect(Solid.on(props.getResetKey, () reset(), { defer: true }))通过Dynamic渲染props.errorComponent ?? ErrorComponent并传入error与reset。注意 Solid 端onCatch的签名是(error: Error) void错误被归一化为Error类型非Error值的原始值会放在cause中这与 React/Vue 的unknown不同写跨框架库时需要注意。默认ErrorComponent的行为三端都内置了相同的ErrorComponent显示 Something went wrong! 提示并提供一个 Show Error / Hide Error 按钮来切换错误详情若抛出值带有非空message会以红色precode展示。默认值在非生产环境下展开process.env.NODE_ENV ! production生产环境下默认隐藏详情见 React 端实现、Vue 端实现、Solid 端实现。更完整的 props 说明error、info、reset参见 ErrorComponent 文档。路由器内部如何使用 CatchBoundaryCatchBoundary不仅是导出给用户的工具它还是路由器错误处理的骨架从源码结构看有两层使用1. 全局错误边界包裹所有 matchreact-router/src/Matches.tsx 中MatchesInner会把整棵路由 match 树包在一个CatchBoundary里作为兜底CatchBoundary getResetKey{() match} onCatch{ process.env.NODE_ENV ! production ? (error) { console.warn( Warning: The following error wasnt caught by any route! At the very least, consider setting an errorComponent in your RootRoute!, ) console.warn(Warning:, error) } : undefined } {matchComponent} /CatchBoundary注意其getResetKey{() match}直接绑定到根路由的 match 对象引用——从源码结构看当路由匹配发生结构性变化新 match 对象生成时全局边界的错误状态会自动复位。如果错误没被任何路由的errorComponent捕获非生产环境会打印上述警告提示至少在RootRoute上配置errorComponent。这个全局边界可以通过 router 选项关闭。router-core/src/router.ts 定义了/** * When true, disables the global catch boundary that normally wraps * all route matches. This allows unhandled errors to bubble up to * top-level error handlers in the browser. * * default false */ disableGlobalCatchBoundary?: boolean适合 Storybook 测试运行器、错误上报服务或需要让错误冒泡到浏览器全局错误处理器的调试场景。各框架包均有对应的行为测试如 disableGlobalCatchBoundary.test.tsx。2. 每条路由的errorComponent边界在 react-router/src/Match.tsx 中若某条路由配置了errorComponent则该 match 会被CatchBoundary否则退化为SafeFragment包裹const ResolvedCatchBoundary routeErrorComponent ? CatchBoundary : SafeFragment ResolvedCatchBoundary getResetKey{() match} errorComponent{routeErrorComponent as any} onCatch{(error, errorInfo) { // 404 错误重新抛出交给 CatchNotFound 处理不显示 errorComponent if (isNotFound(error)) { error.routeId ?? match.routeId throw error } // ... }} 这里体现了几个值得注意的细节路由级边界的 reset key 同样是match引用路由变化时错误状态随之重置NotFound 错误会被识别并重新抛出isNotFound(error)判断从而跳过错误组件、转交给路由内的 not-found 边界CatchNotFound处理嵌套的路由错误会逐层向上直到被最近的errorComponent边界或全局边界接住。小结CatchBoundary提供三个框架一致的 APIgetResetKey声明式重置、children、errorComponent默认ErrorComponent、onCatch无错误渲染 children有错误渲染错误组件错误组件统一收到error与resetprops三端实现分别基于 React 的getDerivedStateFromError、Vue 的onErrorCaptured、Solid 的原生ErrorBoundary错误状态统一采用0 /[error]数组的包装方式来兼容任意抛出值路由器内部用同一个组件构建了两层防线全局边界可经disableGlobalCatchBoundary关闭与每条配置了errorComponent的路由边界NotFound 错误会被显式转发而不触发错误组件。【免费下载链接】router A client-first, server-capable, fully type-safe router and full-stack framework for the web (React and more).项目地址: https://gitcode.com/GitHub_Trending/ro/router创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表