Vue.js: 错误处理与调试
最后更新:2026-08-26
错误处理是任何生产级应用的必备能力——应用崩了能快速定位、优雅降级、通知用户。Vue 3 提供了多层次错误处理:errorHandler(全局)、onErrorCaptured(局部)、errorCaptured 钩子(组件级)。
调试和性能优化同样重要——Vue DevTools 是必备工具,能让你看清组件树、状态、事件、性能瓶颈。
1. 你将学到
- 3 层错误处理:errorHandler / onErrorCaptured / errorBoundary
- Vue DevTools 4 大核心功能(组件树 / 状态 / 事件 / 性能)
- 性能分析(performance API + DevTools)
- Sentry 集成(生产环境错误监控)
- Source Map 错误定位
- 5 大常见调试技巧
2. 一个白屏崩溃"找不到原因"事件
(1) 痛点:1 个 API 报错,整个页面空白
Alice 的后台出了 1 个严重 Bug:
JS
// ProductList.vue
const products = ref([])
onMounted(async () => {
const res = await fetch('/api/products')
products.value = await res.json() // ❌ 后端返回非 JSON → 报错
})
用户体验:
- 整个 ProductList 页面白屏
- 错误信息只在 console
- 用户看到空白页,刷新也没用
产品经理 Charlie:
"Alice,客户抱怨页面白屏。我们需要:1)不让整个页面崩溃,2)显示错误信息,3)上报到监控系统。"
(2) Vue 错误边界解法
VUE
<!-- 父组件:错误边界 -->
<template>
<ErrorBoundary>
<ProductList />
</ErrorBoundary>
</template>
<!-- ErrorBoundary.vue -->
<script setup>
const { onErrorCaptured, ref } = Vue
const error = ref(null)
onErrorCaptured((err, instance, info) => {
console.error('Caught:', err)
// 1. 显示错误信息
error.value = err.message
// 2. 上报到 Sentry
Sentry.captureException(err)
// 3. 阻止向上传播
return false
})
const retry = () => { error.value = null }
</script>
<template>
<div v-if="error" class="error">
<h3>⚠️ Something went wrong</h3>
<p>{{ error }}</p>
<button @click="retry">Retry</button>
</div>
<slot v-else />
</template>
用户体验:
- 错误被捕获,不传播
- 显示友好错误页面
- 错误自动上报 Sentry
- 用户可点击"重试"
(3) 收益
加上错误处理后:
- 白屏崩溃:100% → 0
- 错误定位:console → Sentry 仪表盘
- 用户体验:崩溃 → 友好提示
- 可观测性:所有错误有据可查
3. 3 层错误处理
(1) 全局错误处理:app.config.errorHandler
JS
// main.js
const { createApp } = Vue
import App from './App.vue'
const app = createApp(App)
// ✅ 全局错误处理
app.config.errorHandler = (err, instance, info) => {
console.error('Global error:', err)
console.log('Component:', instance)
console.log('Info:', info) // 'render' / 'watch' / 'lifecycle hook'
// 上报到 Sentry
Sentry.captureException(err)
// 用户通知
showErrorNotification('Something went wrong')
}
(2) 组件级错误:onErrorCaptured
VUE
<!-- ErrorBoundary.vue -->
<script setup>
const { onErrorCaptured, ref } = Vue
const error = ref(null)
// ✅ 捕获子组件错误
onErrorCaptured((err, instance, info) => {
console.error('Boundary caught:', err)
error.value = {
message: err.message,
stack: err.stack,
info
}
// 阻止向上传播
return false
})
const reset = () => { error.value = null }
</script>
<template>
<div v-if="error" class="error-boundary">
<h3>⚠️ {{ error.message }}</h3>
<details>
<summary>Stack trace</summary>
<pre>{{ error.stack }}</pre>
</details>
<button @click="reset">Retry</button>
</div>
<slot v-else />
</template>
(3) 错误层级对比
| 层级 | API | 捕获范围 | 适用 |
|---|---|---|---|
| 全局 | app.config.errorHandler |
所有未捕获错误 | 生产环境必须 |
| 组件 | onErrorCaptured |
子组件错误 | 局部错误边界 |
| 异步 | try/catch / window.onunhandledrejection | Promise 错误 | 手动处理 |
(4) 5 大错误来源
JS
// 1. 渲染错误(模板语法错)
app.config.errorHandler = (err, instance, info) => {
if (info === 'render') console.error('Render error:', err)
}
// 2. 生命周期钩子错误
if (info === 'mounted') console.error('Mounted error:', err)
// 3. watch 回调错误
if (info === 'watcher callback') console.error('Watch error:', err)
// 4. 自定义指令错误
if (info === 'directive') console.error('Directive error:', err)
// 5. 事件处理错误
if (info === 'v-on handler') console.error('Event error:', err)
4. Vue DevTools 调试
(1) 4 大核心面板
| 面板 | 功能 | 用途 |
|---|---|---|
| Components | 组件树、状态、props、emits | 看清组件结构、调试数据 |
| Timeline | 事件、生命周期、性能 | 追踪事件流、性能瓶颈 |
| Pinia/Vuex | Store 状态 | 调试全局状态 |
| Routes | 路由历史 | 调试路由跳转 |
(2) Components 面板使用
TEXT
📖 仅展示
1. 打开 DevTools(F12 / Cmd+Opt+I)
2. 切换到 Vue 标签
3. 点击组件树中的组件
4. 右侧显示:
- State(响应式数据)
- Props
- Emits
- Slots
- Lifecycle
(3) 5 大 DevTools 技巧
JS
// 1. 实时修改 state(开发模式)
// DevTools 中直接编辑,看组件实时响应
// 2. 时间旅行(Pinia)
// 切换到 Pinia 面板 → Base → 可以回放每一步 state 变化
// 3. 性能标记
// import { markRaw } from 'vue'
// 大数据对象用 markRaw 标记,DevTools 不会深遍历
// 4. 组件 highlight
// DevTools → 右上角"眼睛"图标 → 鼠标悬停高亮组件
// 5. 路由跳转
// DevTools → Routing 标签 → 看每次跳转的 path + params
(4) 安装 Vue DevTools
BASH
# Chrome 扩展
# https://chromewebstore.google.com/detail/vuejs-devtools/odjccnclnlddjlajjphfdmhnlhaglgki
# Firefox 扩展
# https://addons.mozilla.org/en-US/firefox/addon/vue-js-devtools/
# 或独立应用(推荐)
# https://devtools.vuejs.org/
5. 性能分析
(1) Vue 3 性能 API
JS
const { onMounted, onUnmounted } = Vue
onMounted(() => {
performance.mark('app-start')
// 你的代码
loadData()
performance.mark('app-end')
performance.measure('app-load', 'app-start', 'app-end')
const measure = performance.getEntriesByName('app-load')[0]
console.log(`App loaded in ${measure.duration}ms`)
})
(2) 5 大性能优化技巧
JS
// 1. v-once:只渲染一次
<h1 v-once>{{ title }}</h1>
// 2. v-memo:缓存子树
<div v-memo="[item.id, item.updatedAt]">
<!-- 只在 id 或 updatedAt 变化时重渲染 -->
</div>
// 3. shallowRef:大数据不深响应
const bigList = shallowRef([...10000 items])
// 4. markRaw:第三方库不响应
const map = markRaw(new Map())
// 5. 懒加载:路由 / 组件
const Heavy = defineAsyncComponent(() => import('./Heavy.vue'))
(3) 5 大性能指标
| 指标 | 目标 | 测量方法 |
|---|---|---|
| FCP(首次内容渲染) | < 1.8s | Lighthouse |
| LCP(最大内容渲染) | < 2.5s | Lighthouse |
| TTI(可交互时间) | < 3.8s | Lighthouse |
| TBT(总阻塞时间) | < 200ms | Lighthouse |
| CLS(布局偏移) | < 0.1 | Lighthouse |
6. Sentry 集成
(1) 安装
BASH
npm install @sentry/vue @sentry/tracing
(2) main.js 配置
JS
const { createApp } = Vue
import * as Sentry from '@sentry/vue'
import { Integrations } from '@sentry/tracing'
import App from './App.vue'
const app = createApp(App)
// 1. 初始化 Sentry(app 必须先创建)
Sentry.init({
app,
dsn: 'https://your-dsn@sentry.io/123',
integrations: [
new Integrations.BrowserTracing()
],
tracesSampleRate: 1.0,
// 生产环境降低采样
// tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0
// Vue 特定配置
logErrors: true,
release: '1.0.0',
environment: process.env.NODE_ENV
})
// 2. 全局错误上报
app.config.errorHandler = (err, instance, info) => {
Sentry.captureException(err, {
extra: {
component: instance?.$options.name,
info
}
})
}
(3) Source Map 配置
JS
// vite.config.js
export default {
build: {
sourcemap: true // 生产环境也生成 source map
}
}
// Sentry 上传 source map
// @sentry/cli releases -o your-org -p your-project files upload-sourcemaps ./dist
(4) 5 大 Sentry 功能
| 功能 | 用途 |
|---|---|
| 错误监控 | 自动捕获所有错误,仪表盘查看 |
| 性能追踪 | 慢请求、Slow DB 查询定位 |
| 会话回放 | 用户操作录屏,看错误现场 |
| 发布健康 | 每次发版错误率对比 |
| 告警 | Slack / Email 通知 |
7. 完整示例:5 大调试技巧
▶ 示例:3 层错误处理(CDN 可演示 ErrorBoundary)
HTML
📖 仅展示
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<style>
.btn { padding: 6px 12px; margin: 4px; background: #42b883; color: white; border: none; border-radius: 4px; cursor: pointer; }
.error { padding: 1rem; background: #fee; border: 1px solid #fcc; color: #c00; border-radius: 4px; margin: 4px 0; }
.buggy { padding: 1rem; background: #f0f9ff; border-radius: 4px; }
</style>
<div id="app">
<button class="btn" @click="triggerError = !triggerError">
{{ triggerError ? '恢复' : '触发子组件错误' }}
</button>
<error-boundary>
<p>ErrorBoundary 之外的组件 - 正常工作</p>
</error-boundary>
<error-boundary>
<buggy-component v-if="triggerError"></buggy-component>
<p v-else class="buggy">ErrorBoundary 内的子组件 - 正常</p>
</error-boundary>
</div>
<script>
const { createApp, ref, onErrorCaptured } = Vue
// ✅ ErrorBoundary 组件
const ErrorBoundary = {
setup(_, { slots }) {
const error = ref(null)
onErrorCaptured((err) => {
error.value = err
return false // 阻止向上传播
})
function reset() { error.value = null }
return { error, reset }
},
template: `
<div>
<div v-if="error" class="error">
⚠️ Error caught: {{ error.message }}
<button @click="reset">重试</button>
</div>
<slot v-else></slot>
</div>
`
}
// 故意出错的子组件
const BuggyComponent = {
setup() {
// 模拟错误:访问 undefined.value
const data = ref(null)
return { data }
},
template: '<div>{{ data.value.foo }}(会报错)</div>'
}
const App = {
components: { ErrorBoundary, BuggyComponent },
setup() {
const triggerError = ref(false)
return { triggerError }
}
}
createApp(App).mount('#app')
</script>
▶ 示例:Vue 3 全局错误处理(⚠️ 需 Vite 项目 main.js)
JS
// main.js
app.config.errorHandler = (err, instance, info) => {
console.error('[Global]', err, info)
// 上报到 Sentry / 自己的日志服务
// Sentry.captureException(err)
}
▶ 示例:5 大错误来源(info 字段)
| info 字段 | 含义 | 例子 |
|---|---|---|
'render' |
渲染错误 | 模板引用未定义变量 |
'mounted' / 'updated' |
生命周期错误 | onMounted 中访问 DOM 失败 |
'watcher callback' |
watch 错误 | watch 回调异常 |
'directive' |
指令错误 | 自定义指令 mounted 异常 |
'v-on handler' |
事件错误 | @click 处理函数 throw |
▶ 示例:5 大性能优化(速查)
JS
// 1. v-once(只渲染一次)
<h1 v-once>{{ title }}</h1>
// 2. v-memo(缓存)
<div v-memo="[item.id]">...</div>
// 3. shallowRef(浅响应)
const list = shallowRef([...])
// 4. markRaw(不响应)
const map = markRaw(new Map())
// 5. 异步组件(懒加载)
const Heavy = defineAsyncComponent(() => import('./Heavy.vue'))
▶ 示例:5 个常见错误速查
| 错误 | 现象 | 解决 |
|---|---|---|
| 白屏崩溃 | 1 个组件错全挂 | 用 ErrorBoundary 包裹 |
| 错误吞掉 | 子组件静默失败 | onErrorCaptured 不要忘 return false |
| Source Map 缺失 | 生产错误无法定位 | vite build sourcemap: true |
| 性能差 | 列表 1000 项卡顿 | v-memo + shallowRef |
| 内存泄漏 | 组件销毁后定时器还在 | onUnmounted 清理 |
▶ 示例:5 大调试场景
| 场景 | 工具 |
|---|---|
| 组件状态异常 | Vue DevTools → State |
| 事件不触发 | Vue DevTools → Timeline |
| 路由跳转错 | DevTools → Routing |
| 生产环境崩溃 | Sentry 仪表盘 |
| 性能慢 | Lighthouse + DevTools Performance |
❓ 常见问题
Q errorHandler 能捕获异步错误吗?
A 不能。
errorHandler 只捕获 Vue 内部的同步错误。异步错误(Promise / setTimeout)需要 try/catch 或 window.addEventListener('unhandledrejection')。Q onErrorCaptured 必须 return false 吗?
A 不是必须。return false 阻止错误向上传播。return true(或不 return)让错误继续向上传播到全局 errorHandler。
Q Vue DevTools 在生产环境能用吗?
A 能。生产构建会自动启用 DevTools(不影响性能)。但建议在生产环境用
Vue.config.devtools = false 关闭(避免暴露内部结构)。Q Sentry 怎么收费?
A 免费版每月 5,000 events + 10,000 performance transactions。中型项目够用。超出后按量计费。
Q v-memo 和 v-for 怎么配合?
A
<div v-for="item in items" :key="item.id" v-memo="[item.id, item.updatedAt]">,只有 id 或 updatedAt 变化时才重渲染这个 div。Q 怎么调试 SSR 应用?
A VS Code 调试器 + Node.js inspect 模式。或 Sentry 捕获服务端错误。或在 Nuxt 3 中用 Nitro 调试工具。
Q onErrorCaptured 在 Vue 3 和 Vue 2 区别?
A Vue 2 是
errorCaptured 钩子,Vue 3 是 onErrorCaptured 组合式 API。Vue 3 还新增全局 app.config.errorHandler。📖 小节
- 3 层错误处理:全局(errorHandler)+ 组件(onErrorCaptured)+ 异步(try/catch)
- 5 大错误来源:render / 生命周期 / watch / 指令 / 事件
- Vue DevTools 4 大面板:Components / Timeline / Pinia / Routes
- 5 大性能优化:v-once / v-memo / shallowRef / markRaw / 懒加载
- Sentry 集成:自动捕获 + 性能追踪 + Source Map
- 5 大性能指标:FCP / LCP / TTI / TBT / CLS
- 生产环境必备:ErrorBoundary + Sentry + Source Map
📝 作业
-
基础题(难度⭐) 实现一个 ErrorBoundary 组件:
- onErrorCaptured 捕获子组件错误
- 显示友好的错误页面
- 提供"重试"按钮
-
进阶题(难度⭐⭐) 实现完整的错误处理系统:
- 全局 errorHandler 上报到控制台
- 组件级 ErrorBoundary
- 异步错误 try/catch
- 5 种错误来源分类
-
挑战题(难度⭐⭐⭐) 实现完整的"生产级"调试 + 错误监控:
- ErrorBoundary 组件 + 3 个不同位置
- Sentry 集成(生产环境)
- Source Map 配置
- Vue DevTools 5 大调试技巧
- 5 大性能优化(v-memo / shallowRef / markRaw / 懒加载 / v-once)
- 5 大性能指标监控(Lighthouse 集成)