首页
/ Nuxt error.vue 深度解析:自定义错误页与 NuxtError 错误的完整处理机制

Nuxt error.vue 深度解析:自定义错误页与 NuxtError 错误的完整处理机制

2026-09-04 12:32:20作者:邓越浪Henry

本文基于 Nuxt 官方目录结构文档 error.vue 展开,讲清楚 app/error.vue 文件如何接管应用运行时的所有错误渲染:从 NuxtError 对象的字段含义、createError 的用法,到该文件在构建时如何被解析、在运行时如何被挂载渲染,并对照 Nuxt 源码解释错误页触发、清除与爬虫友好的完整调用链。

error.vue 是什么:接管默认错误页的入口文件

在应用生命周期中,总有一些错误会在运行时意外出现:路由不存在、useFetch 抛出的 HTTP 错误、插件或组件里的未捕获异常。Nuxt 通过 app/error.vue 文件让你覆盖默认错误页,以统一、友好的方式呈现这些错误。

一个最小的自定义错误页写法如下(与官方文档示例一致):

<script setup lang="ts">
import type { NuxtError } from '#app'

const props = defineProps<{ error: NuxtError }>()
</script>

<template>
  <div>
    <h1>{{ error.status }}</h1>
    <NuxtLink to="/">Go back home</NuxtLink>
  </div>
</template>

文件需要放在应用的 app/ 目录(默认即 ~/app/error.vue)下。它只有一个 prop——error,类型是 NuxtError,其中包含了你要处理的全部错误信息。

它不是路由:为什么不能放进 pages 目录

官方文档特别强调了一个容易被误解的点:虽然叫 "error page",但它不是一个路由,不应放在 ~/pages 目录中;同理,也不应在这个文件里使用 definePageMeta。原因从源码结构可以直接印证:error.vue 不参与文件路由系统的解析,而是作为独立组件被挂载到应用根组件内部——当 nuxt-root 检测到 payload.error 有值时,用错误页组件替换掉正常的应用组件树(见 nuxt-root.vueErrorComponent v-else-if="error" 的分支逻辑)。

不过文档同时指出:你可以在错误页里使用布局,方式是借助 <NuxtLayout> 组件并指定布局名称,从而让错误页复用应用的整体外观(例如导航栏、主题样式)。

错误解析流程:源码如何找到你的 error.vue

从源码看,error.vue 的解析发生在构建准备阶段。core/app.ts 中的 resolveAppComponents 会按 layer 顺序查找 error 文件,找不到时回退到内置默认错误页:

// packages/nuxt/src/core/app.ts
// Resolve error component
app.errorComponent ||= await findPath(layerDirs.map(d => join(d.app, 'error')))
  ?? resolve(nuxt.options.appDir, 'components/nuxt-error-page.vue')

这段代码说明了两个重要行为:

  1. 层级(Layers)优先findPath 会遍历所有 layer 的 app/ 目录,因此基座 layer 的 error.vue 会被上层应用同名的文件覆盖——这与 Nuxt 其他配置合并规则一致;
  2. 内置兜底:项目没有提供 app/error.vue 时,Nuxt 使用内置的 nuxt-error-page.vue 作为错误页。该默认页面会按状态码选择模板:status === 404 时使用 error-404.vue,其余错误使用 error-500.vue,并在开发模式下展示堆栈信息。

解析结果随后被 core/templates.ts 中的 errorComponentTemplate 生成 #build/error-component.mjs 虚拟模块,供根组件 import 使用:

export const errorComponentTemplate: NuxtTemplate = {
  filename: 'error-component.mjs',
  dependsOn: [],
  getContents: ctx => genExport(ctx.app.errorComponent!, ['default']),
}

开发模式下,对 error.vue 的增删改还有专门的监听处理:core/nuxt.tserror.vueapp.vueapp.config.ts 并列为核心文件,新增或删除时会触发 Nuxt 重启;core/builder.ts 则会在文件变更时清空缓存的 app.errorComponent,让解析结果重新生效。这意味着修改 error.vue 后,开发服务器会可靠地重新解析,不会出现“改了错误页不生效”的问题。

NuxtError 对象:error prop 的完整字段

官方文档给出了错误页 error prop 的核心类型。结合 app/types.ts 中的完整声明,NuxtError 在文档版本基础上还包含 headersbody 等面向 HTTP 响应的字段:

interface NuxtError {
  status: number        // HTTP 状态码,范围 [100...599]
  fatal: boolean        // 是否为致命错误(未处理或显式标记 fatal 时为 true)
  unhandled: boolean    // 是否未被应用处理
  statusText?: string   // HTTP 状态文本(reason phrase)
  data?: unknown        // 附加数据,会序列化进错误 JSON 的 data 字段
  cause?: unknown       // 错误链上的原始错误
}

各字段的语义如下:

字段 类型 说明
status number HTTP 状态码,缺省为 500(见 createError 实现)
statusText string? HTTP 状态文本,如 Not Found
fatal boolean 致命错误标记;源码中默认取 details.fatal ?? !!this.unhandled
unhandled boolean? 错误未被应用捕获处理时置为 true
data unknown 自定义数据,会随错误 JSON 序列化
cause unknown 原始错误对象(错误链)
headers / body 完整版类型中附加的 HTTP 头与响应体字段(见 types.ts

值得注意的序列化细节:NuxtErrortoJSON() 方法在错误被标记为 unhandled不会输出 messagedata(消息统一为 'HTTPError'),这是避免将内部错误细节泄露给客户端的安全设计(见 error.tstoJSON 实现)。因此在错误页中读取 error.data 时,应意识到 SSR 场景下序列化后的对象可能不含这部分信息。

另外,NuxtError 类刻意将 name 覆盖为 HTTPError——因为 h3/Nitro 按构造器名识别错误类型,SSR 过程中抛出的 Nuxt 错误只有保持这个名字才能被映射为正确的 HTTP 响应状态码。

制造错误:createError 与 data 字段的使用规范

官方文档强调了一条重要的实践规范:如果你要抛出一个带自定义字段的错误,自定义字段会丢失,应该把它们放进 data。正确写法:

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

createError 的实现在 composables/error.ts 中,它接受字符串、Error 或错误详情对象三种输入,并做了幂等处理(传入已是 NuxtError 时直接返回):

export const createError = <DataT = unknown>(error: string | Error | NuxtErrorDetails<DataT>): NuxtError<DataT> => {
  if (isNuxtError<DataT>(error)) { return error }
  return typeof error === 'string'
    ? new NuxtError<DataT>(error)
    : new NuxtError<DataT>(error.message ?? '', error as NuxtErrorDetails<DataT>)
}

NuxtError 构造函数可以看出几个默认值行为,供你在使用时参考:

  • status 缺省时回退到 cause 的 status,最终缺省 500,并经过 sanitizeStatusCode 约束在 [100...599] 范围内;
  • message 的推断顺序为:显式 messagecause.messagestatusText → 由 'HTTPError status statusText' 拼接兜底;
  • 旧的 statusCode / statusMessage 字段仍被接受,但已标记为 deprecated,新代码应使用 status / statusText

配合错误页的 data 约定,错误页中就可以安全地读取 error.data 来渲染业务化信息,例如区分“页面不存在”与“登录过期”。

错误的运行时生命周期:从抛出到渲染

理解了文件与类型后,再看错误页是如何被触发和清除的。整条链路可以从 nuxt-root.vue 入手:

<Suspense @resolve="onResolve">
  <ErrorComponent v-else-if="error" :error="error" />
  <AppComponent v-else />
</Suspense>

其中 error 来自 useError()(即 nuxtApp.payload.error 的响应式引用)。根组件注册了 onErrorCaptured

  • 服务端渲染(SSR)中捕获到的任何组件错误,都会调用 showError(err) 写入 payload,并返回 false 阻止错误中断渲染——这正是 SSR 能返回带状态码的完整错误页 HTML 的原因;
  • 客户端捕获的致命或未处理fatal || unhandled)的 Nuxt 错误同样触发 showError
  • 特殊处理:如果当前是爬虫 User-Agent,Nuxt 只调用 app:error hook 通知错误而不渲染错误页(_notifyCrawlerError),让爬虫索引到服务端已渲染的 HTML 而不是错误页。

showError 与 clearError

两个关键 composable 均在 composables/error.ts

export const showError = <DataT = unknown>(
  error: string | Error | NuxtErrorDetails<DataT>,
): NuxtError<DataT> => {
  const nuxtError = createError<DataT>(error)
  // 客户端触发 app:error hook
  // error.value ||= nuxtError  → 写入 payload.error,驱动 error.vue 渲染
}

showError 将错误写入 payload.errornuxt-root 检测到该值后立刻用你的 error.vue 替换应用组件树。对应地,clearError 会触发 app:error:cleared hook、可选地执行 router.replace(redirect) 重定向,然后把 payload.error 置空,错误页随之消失、应用恢复正常渲染。

插件与入口层面的错误兜底

错误页的覆盖面不止组件错误。从源码结构看:

  • entry.ts 在服务端和客户端两个入口都捕获插件执行与挂载阶段的异常,统一走 app:error hook 并写入 nuxt.payload.error
  • nuxt.tsapplyPlugins 循环内的注释“short circuit if we are not rendering error.vue”表明:当已有错误待渲染时,后续插件抛出的异常会被短路合并,避免掩盖最初的错误,保证错误页展示的仍是第一个根因错误。

相关的 Hook

在错误处理链路上,可以订阅以下运行时 Hook 做日志、监控上报:

Hook 触发时机
app:error 错误被创建并准备渲染错误页时(SSR 与客户端均触发)
app:error:cleared clearError 被调用、错误清除时
vue:error 通过 Vue 的 onErrorCaptured 捕获到组件级错误时

默认错误页长什么样:当你不提供 error.vue 时

没有自定义 error.vue 时,Nuxt 使用的内置错误页 nuxt-error-page.vue 做了不少默认处理,值得了解以便决定是否需要覆盖:

const status = Number(_error.status || 500)
const is404 = status === 404
const statusText = _error.statusText ?? (is404 ? 'Page Not Found' : 'Internal Server Error')
const description = _error.message || _error.toString()
const stack = import.meta.dev && !is404 ? _error.description || `<pre>${stacktrace}</pre>` : undefined

即:404 展示“Page Not Found”模板,其他错误展示“Internal Server Error”模板,且仅在开发模式下对非 404 错误附带格式化堆栈(内部模块行以特殊样式标注)。模板本身由 ui-templates 包生成,404 页还内联了 modulepreload 补丁和“返回上一页”的交互脚本。如果你的业务错误页只需要定制文案与样式,参照这份默认实现即可快速上手。

小结与实践要点

  • app/error.vue 是错误页的唯一自定义入口:放在 app/ 目录(可被 layer 覆盖),不要放进 pages/,不要使用 definePageMeta;布局需求用 <NuxtLayout> 组件实现。
  • 错误页通过 error prop 接收 NuxtError;自定义业务字段请放入 createError({ ..., data: {...} })data 中,否则会在序列化时丢失。
  • 错误页的触发不依赖路由系统:payload.error 有值时,nuxt-root.vue 会用错误页组件替换整个应用组件树;showError / clearError 是驱动其出现与消失的官方 API。
  • 未处理错误在序列化为 HTTP 响应时会隐藏 messagedata,错误页设计时应对 error.data 可能为 undefined 的情况保持容忍。
  • 爬虫访问时 Nuxt 有意不渲染错误页,只通过 app:error hook 通知,保证 SEO 内容不被错误页覆盖。
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
915
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388