Skip to content

前端错误处理方案

这篇笔记整理了在 Vine 项目中落地的前端错误处理方案,从中抽象出可以在其他工程中复用的实践思路。

按错误类型拆分处理

整体思路是:按错误来源拆分责任请求错误渲染错误分别在各自最合适的层级集中处理。

错误类型处理方案实现位置
请求错误TanStack Query 全局回调QueryCache.onError / MutationCache.onError
渲染错误React Router errorElement路由配置 errorElement

请求错误

通过 QueryCacheMutationCacheonError 回调统一处理所有 HTTP 请求错误,在一个地方集中做「HTTP 状态码 → UI 行为」的映射。

处理策略

错误类型处理方式
401 Unauthorized(未认证 - 不知道你是谁)清除登录态,跳转登录页
403 Forbidden(未授权 - 知道你是谁,但你没权限)Toast 提示无权限
404 Not FoundToast 提示资源不存在
5xx Server ErrorToast 提示服务器错误
网络错误Toast 提示网络异常
业务错误Toast 显示后端返回的 message

Router 内创建 QueryClient

请求错误处理(如 401 自动跳转登录页)需要使用 navigate。在 Vine 的实现中,直接把 QueryClientProvider 放到 Router 树内部创建,避免「路由桥接」这类全局可变注入带来的时序窗口问题。

tsx
// src/core/router/RootLayout.tsx
export function RootLayout() {
  const navigate = useNavigate()
  const [queryClient] = useState(() => createQueryClient(navigate))

  return (
    <QueryClientProvider client={queryClient}>
      <VAppLayout />
    </QueryClientProvider>
  )
}

跳过全局处理

某些组件需要自行处理错误时,可以通过 meta 字段通知全局 handler:

tsx
const { data, error, refetch } = useQuery({
  queryKey: ['orders'],
  queryFn: fetchOrders,
  meta: { skipGlobalErrorHandler: true }
})

if (error) {
  return <CustomErrorUI error={error} onRetry={refetch} />
}

渲染错误

通过 React Router 的 errorElement 捕获路由组件内的渲染错误(例如 TypeError)。这类错误通常是开发阶段遗漏的边界情况,应该在开发期就尽量消灭,但仍然需要有兜底。

配置方式

tsx
// src/core/router/routes.tsx
export const routes: RouteObject[] = [
  {
    path: '/',
    element: <RootLayout />,
    errorElement: <RouteErrorFallback />,
    children: [...]
  }
];

典型触发场景

  • 组件渲染时抛出异常(如 TypeError: xxx.map is not a function
  • 数据处理异常(空值、类型不符等)
  • 第三方库渲染错误

项目结构示例

src/core/
├── http/
│   ├── axios/
│   │   ├── instance.ts              # Axios 实例
│   │   └── interceptors/
│   │       ├── request.ts           # 请求拦截(添加 token)
│   │       └── response.ts          # 响应拦截(解包数据)
│   └── query/
│       ├── queryClient.ts           # createQueryClient + 全局错误回调
│       └── errorHandlers.ts         # 请求错误处理函数
└── router/
    ├── routes.tsx                   # 路由配置(含 errorElement)
    ├── RootLayout.tsx               # Router 内创建 QueryClientProvider
    └── RouteErrorFallback.tsx       # 渲染错误 UI

防御性编码

虽然 RouteErrorFallback 能捕获渲染错误,但触发它意味着页面已经崩溃、用户看到的是兜底 UI。因此在业务代码中仍然需要做好防御性编码。

tsx
// ❌ 危险:data.list 如果是非可迭代值会抛出 TypeError
const items = [...(data?.list || [])]

// ✅ 安全:确保是数组再使用
const items = Array.isArray(data?.list) ? data.list : []

核心原则是:凡是来源不受你完全控制的数据(接口返回、URL 参数、本地存储等),在使用前都要做类型和空值检查。

Released under the MIT License.