Next.js 错误处理完全指南(Pages 与 App Router)

Next.js 的混合渲染模型带来独特的错误处理挑战。本文覆盖 Pages Router 与 App Router 两套体系的错误边界、服务端组件异常、API 路由错误与生产监控方案。

最佳实践
Web 构建模块与工具协作插画

App Router 用文件约定分层兜底:error.tsx 捕获路由段错误、global-error.tsx 兜住根布局错误、not-found.tsx 处理 404;服务端错误的堆栈在生产环境自动脱敏。Pages Router 则用 _error.tsx 与 Error Boundary 组件。

Next.js 错误的特殊性

混合渲染让错误可能发生在四个位置:服务端渲染(SSR/RSC)、客户端组件、API 路由/Server Action、构建期。每层的捕获机制不同——这正是 Next.js 错误处理看起来"复杂"的原因:不是规则多,是层面多。

App Router:文件即错误边界

app/
├── layout.tsx
├── page.tsx
├── error.tsx          ← 本路由段的错误边界
├── global-error.tsx   ← 根错误边界(捕获 layout 的错误)
├── not-found.tsx      ← notFound() 触发的 404 页
└── dashboard/
    └── error.tsx      ← 嵌套路由的局部边界
// app/dashboard/error.tsx
'use client';   // 错误边界必须是客户端组件

export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  return (
    <div>
      <h2>出了点问题</h2>
      <button onClick={reset}>重试</button>
    </div>
  );
}

要点:错误边界按路由段就近捕获——dashboard 出错不影响侧边栏导航;reset() 尝试重新渲染该段;生产环境下 RSC 错误只透传 digest 编号而非消息,敏感信息天然脱敏。

数据获取与 Server Action 的错误

// 服务端组件:手动处理预期内错误
async function Page({ params }) {
  const user = await getUser(params.id).catch(() => null);
  if (!user) notFound();          // 渲染 not-found.tsx
  return <Profile user={user} />;
}

// Server Action:返回错误对象而非抛出
async function updateProfile(formData: FormData) {
  'use server';
  const parsed = schema.safeParse(formData);
  if (!parsed.success) {
    return { error: 'VALIDATION_FAILED', fields: parsed.error.flatten() };
  }
  // ...
}

Server Action 的错误对象要可序列化;预期内错误返回结构化结果,预期外错误抛出交给最近的 error.tsx。

Pages Router 体系

老路由体系下:自定义 pages/_error.tsx 渲染错误页(SSR 与客户端通用);getServerSideProps 里返回 notFound: true 触发 404;客户端组件树用 React Error Boundary 类组件包裹。API 路由的错误处理同 Express 思路:try/catch + 状态码 + 结构化 JSON。

串起浏览器报错与服务端异常

Next.js 的浏览器错误边界和服务端日志记录的是不同现场。浏览器端接入观测云 RUM,并上传与发布版本匹配的 SourceMap;Node.js 服务端另接入 APM 和日志采集。生产错误中的 digest 可作为补充检索线索,不能替代分布式 trace_id。跨端串联还需要追踪头传递与后端插桩配合,避免把服务端错误堆栈直接暴露给浏览器。

接入文档:Web RUM 接入 · SourceMap 配置 · Node.js APM 接入 · 日志与链路关联。

常见问题(FAQ)

Q:error.tsx 为什么必须是客户端组件?

错误边界基于 React 的 componentDidCatch/getDerivedStateFromError 机制,只有客户端组件支持。'use client' 声明即可,边界内依然可以渲染服务端组件作为子元素。

Q:global-error.tsx 和 error.tsx 的分工?

error.tsx 管不了同级的 layout.tsx(边界在布局之内);根布局自身出错只有 global-error.tsx 能接住——它必须包含完整的 html/body 标签。生产环境两个都要有。

Q:开发环境能看到详细错误,生产只有 digest 怎么办?

这是 RSC 的安全特性(防止堆栈泄露)。用 digest 关联服务端日志:在 error.tsx 中把 digest 上报到日志/监控平台,与服务端错误日志按 digest 匹配还原现场。


参考文档

本文依据公开文档整理,示例需按项目依赖版本验证;不将文档中的性能描述视为本项目实测结果。

延伸阅读

获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

在线开通,按量计费,真正的云服务!

立即开始

选择观测云版本

代码托管平台