错误处理

error.js 文件约定允许您在嵌套路由中优雅地处理意外的运行时错误。

  • 自动使用 React 错误边界包裹路由片段及其嵌套子元素
  • 利用文件系统层级结构创建针对特定片段的错误 UI,实现精细控制
  • 将错误隔离在受影响片段内,同时保持应用程序其余部分正常运行
  • 添加功能以尝试从错误中恢复,无需完全刷新页面

通过在路由片段内添加 error.js 文件并导出 React 组件来创建错误 UI:

error.js 特殊文件
'use client' // 错误组件必须是客户端组件

import { useEffect } from 'react'

export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string }
  reset: () => void
}) {
  useEffect(() => {
    // 将错误记录到错误报告服务
    console.error(error)
  }, [error])

  return (
    <div>
      <h2>出错了!</h2>
      <button
        onClick={
          // 尝试通过重新渲染片段来恢复
          () => reset()
        }
      >
        重试
      </button>
    </div>
  )
}

error.js 工作原理

error.js 工作原理
  • error.js 会自动创建一个 React 错误边界包裹嵌套的子片段或 page.js 组件
  • error.js 文件导出的 React 组件将作为备用组件使用
  • 如果在错误边界内抛出错误,错误会被捕获,并渲染备用组件
  • 当备用错误组件激活时,错误边界上方的布局会保持其状态并维持交互性,错误组件可以显示恢复功能

从错误中恢复

有时错误原因可能是暂时的。这种情况下,简单地重试可能会解决问题。

错误组件可以使用 reset() 函数提示用户尝试从错误中恢复。执行该函数时,会尝试重新渲染错误边界的内容。如果成功,备用错误组件将被重新渲染的结果替换。

'use client'

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

嵌套路由

通过特殊文件创建的 React 组件会在特定的嵌套层级结构中渲染。

例如,包含 layout.jserror.js 文件的两个嵌套路由段会按照以下简化的组件层级结构渲染:

嵌套错误组件层级

嵌套组件层级结构会影响嵌套路由中 error.js 文件的行为:

  • 错误会向上冒泡到最近的父级错误边界。这意味着 error.js 文件会处理其所有嵌套子片段中的错误。通过在路由的不同层级文件夹中放置 error.js 文件,可以实现更精细或更粗略的错误 UI。
  • error.js 边界不会处理同一片段中 layout.js 组件抛出的错误,因为错误边界嵌套在该布局组件的内部

处理布局中的错误

error.js 边界不会捕获同一片段中 layout.jstemplate.js 组件抛出的错误。这种有意的层级结构确保当错误发生时,兄弟路由间共享的重要 UI(如导航)保持可见和功能正常。

要处理特定布局或模板中的错误,请在布局的父级片段中放置 error.js 文件。

要处理根布局或模板中的错误,请使用 error.js 的变体 global-error.js

处理根布局中的错误

根目录下的 app/error.js 边界不会捕获根 app/layout.jsapp/template.js 组件抛出的错误。

要专门处理这些根组件中的错误,请使用位于根 app 目录下的 error.js 变体 app/global-error.js

与根 error.js 不同,global-error.js 错误边界会包裹整个应用程序,其备用组件在激活时会替换根布局。因此需要注意,global-error.js 必须定义自己的 <html><body> 标签。

global-error.js 是最不精细的错误 UI,可以被视为整个应用程序的"全局捕获"错误处理。由于根组件通常较少动态变化,且其他 error.js 边界会捕获大多数错误,因此它很少被触发。

即使定义了 global-error.js,仍然建议定义一个根 error.js,其备用组件将在根布局内部渲染,包含全局共享的 UI 和品牌标识。

'use client'

export default function GlobalError({
  error,
  reset,
}: {
  error: Error & { digest?: string }
  reset: () => void
}) {
  return (
    <html>
      <body>
        <h2>出错了!</h2>
        <button onClick={() => reset()}>重试</button>
      </body>
    </html>
  )
}

须知:

  • global-error.js 仅在生产环境中生效。在开发环境下,会显示我们的错误覆盖层。

处理服务端错误

如果在服务端组件中抛出错误,Next.js 会将 Error 对象(在生产环境中会去除敏感错误信息)作为 error 属性传递给最近的 error.js 文件。

保护敏感错误信息

在生产环境中,传递给客户端的 Error 对象仅包含通用的 messagedigest 属性。

这是一种安全预防措施,避免将错误中可能包含的敏感细节泄露给客户端。

message 属性包含关于错误的通用信息,digest 属性包含自动生成的错误哈希值,可用于匹配服务端日志中的相应错误。

在开发环境下,传递给客户端的 Error 对象会被序列化,并包含原始错误的 message,以便于调试。

On this page