Nuxt3 中文课程 《实战全栈开发简书》.

错误处理

学习如何在 Nuxt 中捕获和处理错误。

Nuxt 3 是一个全栈框架,这意味着在不同的上下文中可能会发生几种无法预防的用户运行时错误:

  • Vue 渲染生命周期中的错误(SSR 和 CSR)
  • Nitro 服务器生命周期中的错误(server/ 目录)
  • 服务器和客户端启动错误(SSR + CSR)
  • 下载 JS chunk 时出错
SSR 代表 Server-Side RenderingCSR 代表 Client-Side Rendering

Vue 渲染生命周期

你可以使用 onErrorCaptured 来捕获 Vue 错误。

此外,Nuxt 还提供了一个 vue:error 钩子,如果有任何错误传播到顶层,将会调用该钩子。

如果你使用的是错误报告框架,你可以通过 vueApp.config.errorHandler 提供一个全局处理程序。它将接收所有的 Vue 错误,即使这些错误已经被处理。

plugins/error-handler.ts
export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.vueApp.config.errorHandler = (error, instance, info) => {
    // 处理错误,例如上报到一个服务
  }

  // 也可以这样
  nuxtApp.hook('vue:error', (error, instance, info) => {
    // 处理错误,例如上报到一个服务
  })
})
需要注意的是,vue:error 钩子基于 onErrorCaptured 生命周期钩子。

启动错误

如果启动 Nuxt 应用时出现任何错误,Nuxt 将会调用 app:error 钩子。

这包括:

  • 运行 Nuxt 插件
  • 处理 app:createdapp:beforeMount 钩子
  • 将你的 Vue 应用渲染为 HTML(在 SSR 期间)
  • 挂载应用程序(在客户端),不过你应该使用 onErrorCapturedvue:error 来处理这种情况
  • 处理 app:mounted 钩子

Nitro 服务器生命周期

目前你无法为这些错误定义一个服务器端处理程序,但可以渲染一个错误页面,参见 渲染错误页面 部分。

JS chunk 错误

由于网络连接故障或新部署(使旧的散列 JS chunk URL 失效),你可能会遇到块加载错误。Nuxt 提供了内置支持来处理块加载错误,当在路由导航过程中某个块加载失败时,它会执行硬刷新。

你可以通过将 experimental.emitRouteChunkError 设置为 false(完全禁用对这些错误的处理)或 manual(手动处理错误)来更改此行为。如果你想手动处理块加载错误,你可以查看 自动实现 来获取一些想法。

错误页面

当 Nuxt 遇到致命错误(服务器上的任何未处理错误,或客户端上使用 fatal: true 创建的错误),它将要么渲染一个 JSON 响应(如果使用 Accept: application/json 头部请求),要么触发一个全屏错误页面。

在以下情况下,可能会在服务器生命周期中发生错误:

  • 处理 Nuxt 插件
  • 将你的 Vue 应用程序渲染为 HTML
  • 服务器 API 路由抛出错误

它也可能在客户端上发生以下情况:

  • 处理 Nuxt 插件
  • 在挂载应用程序之前(app:beforeMount 钩子)
  • 如果错误没有使用 onErrorCapturedvue:error 钩子进行处理,则在挂载应用程序时
  • 在浏览器中初始化和挂载 Vue 应用程序(app:mounted)。
了解所有 Nuxt 生命周期钩子。

通过在应用程序源目录中添加 ~/error.vue,可以自定义默认错误页面,与 app.vue 放在一起。

error.vue
<script setup lang="ts">
const props = defineProps({
  error: Object
})

const handleError = () => clearError({ redirect: '/' })
</script>

<template>
  <div>
    <h2>{{ error.statusCode }}</h2>
    <button @click="handleError">清除错误</button>
  </div>
</template>
尽管它被称为“错误页面”,但它不是一个路由,不应该放在 ~/pages 目录中。出于同样的原因,你不应该在此页面中使用 definePageMeta

错误页面有一个单一的 prop - error,其中包含一个待处理的错误。

error 对象提供以下字段:

{
  url: string
  statusCode: number
  statusMessage: string
  message: string
  description: string
  data: any
}

如果你有一个带有自定义字段的错误,它们将会丢失;你应该将它们分配给 data

throw createError({
  statusCode: 404,
  statusMessage: 'Page Not Found',
  data: {
    myCustomField: true
  }
})

对于自定义错误,我们强烈建议使用 onErrorCaptured 组合式,它可以在页面/组件设置函数中调用,或者使用 vue:error 运行时的 Nuxt 钩子,可以在 Nuxt 插件中进行配置。

plugins/error-handler.ts
export default defineNuxtPlugin(nuxtApp => {
  nuxtApp.hook('vue:error', (err) => {
    //
  })
})

当你准备移除错误页面时,你可以调用 clearError 辅助函数,它接受一个可选的路径进行重定向(例如,如果你想导航到一个“安全”的页面)。

在使用任何依赖于 Nuxt 插件的内容之前,请确保检查,例如 $routeuseRouter,因为如果插件抛出错误,那么插件将不会重新运行,直到你清除错误为止。
如果你在 Node 16 上运行,并且在渲染错误页面时设置了任何 cookie,它们将会覆盖先前设置的 cookie。我们建议使用更新的 Node 版本,因为 Node 16 在2023年9月达到了生命周期终点。

错误工具

useError

TS Signature
function useError (): Ref<Error | { url, statusCode, statusMessage, message, description, data }>

此函数将返回正在处理的全局 Nuxt 错误。

了解有关 useError 组合式的更多信息。

createError

TS Signature
function createError (err: { cause, data, message, name, stack, statusCode, statusMessage, fatal }): Error

使用附加元数据创建错误对象。它可以在你的应用程序的 Vue 和服务器部分中使用,并且意在被抛出。

如果你抛出使用 createError 创建的错误:

  • 在服务器端,它将触发一个全屏错误页面,你可以使用 clearError 清除该错误。
  • 在客户端,它将抛出一个非致命错误供你处理。如果你需要触发一个全屏错误页面,你可以通过设置 fatal: true 来实现。
pages/movies/[slug].vue
<script setup lang="ts">
const route = useRoute()
const { data } = await useFetch(`/api/movies/${route.params.slug}`)

if (!data.value) {
  throw createError({
    statusCode: 404,
    statusMessage: 'Page Not Found'
  })
}
</script>
了解有关 createError 实用程序的更多信息。

showError

TS Signature
function showError (err: string | Error | { statusCode, statusMessage }): Error

你可以在客户端的任何地方调用此函数,或者(在服务器端)直接在中间件、插件或 setup() 函数中使用。它将触发一个全屏错误页面,你可以使用 clearError 清除该错误。

建议使用 throw createError()

了解有关 showError 实用程序的更多信息。

clearError

TS Signature
function clearError (options?: { redirect?: string }): Promise<void>

此函数将清除当前正在处理的 Nuxt 错误。它还接受一个可选的路径进行重定向(例如,如果你想导航到一个“安全”的页面)。

了解有关 clearError 实用程序的更多信息。

在组件中渲染错误

Nuxt 还提供了一个 <NuxtErrorBoundary> 组件,允许你在应用程序中处理客户端错误,而无需用错误页面替换整个站点。

该组件负责处理其默认插槽中发生的错误。在客户端上,它将阻止错误冒泡到顶层,并渲染 #error 插槽。

#error 插槽将接收 error 作为 prop。(如果你设置 error = null,它将触发重新渲染默认插槽;你需要确保错误已经完全解决,否则错误插槽将被再次渲染。)

如果你导航到另一个路由,错误将自动清除。
pages/index.vue
<template>
  <!-- 一些内容 -->
  <NuxtErrorBoundary @error="someErrorLogger">
    <!-- 使用默认插槽来渲染你的内容 -->
    <template #error="{ error, clearError }">
      你可以在此处局部显示错误:{{ error }}
      <button @click="clearError">
        这将清除错误。
      </button>
    </template>
  </NuxtErrorBoundary>
</template>
Read and edit a live example in Docs > Examples > Advanced > Error Handling.