在 Vue 3 中,并没有一个原生的"全局 try...catch"语法可以直接包裹整个应用,因为 JavaScript 的 try...catch 只能捕获同步阻塞代码。
不过,为了实现类似"全局捕获所有错误"的效果,保障页面不卡顿且用户体验友好,我们需要组合使用 Vue 提供的生命周期钩子 、全局配置 以及原生浏览器事件。
以下是实现 Vue 3 全局错误捕获的完整方案:
1. Vue 全局错误处理器 (核心)
这是 Vue 3 官方提供的兜底方案,用于捕获组件渲染、侦听器以及生命周期钩子中的未处理异常。
// main.js 或 main.ts
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
// 设置全局错误处理器
app.config.errorHandler = (err, instance, info) => {
// err: 错误对象
// instance: 出错的组件实例
// info: Vue 特定的错误信息(如错误来源的生命周期钩子)
console.error('Vue 全局捕获到错误:', err, info)
// 这里可以进行错误上报
// reportErrorToService(err, info)
}
app.mount('#app')
2. 捕获未处理的 Promise 异常
try...catch 无法捕获异步 Promise 中被 reject 但没有被 .catch() 的错误。我们需要监听 unhandledrejection 事件。
// main.js 或单独的 error-handler.js
window.addEventListener('unhandledrejection', (event) => {
console.error('未处理的 Promise 错误:', event.reason)
// 阻止浏览器控制台的默认报错(可选,通常用于生产环境隐藏细节)
// event.preventDefault();
// 上报错误
// reportError(event.reason);
});
3. 捕获静态资源与脚本错误
对于 <script> 标签加载的 JS/CSS 错误,或者图片加载失败等,需要监听 window 的 error 事件。
window.addEventListener('error', (event) => {
// 区分是脚本错误还是资源加载错误
if (event.target !== window) {
// 资源加载错误 (img, script, css)
console.error('资源加载失败:', event.target.src || event.target.href)
} else {
// JS 运行时错误
console.error('JS 运行时错误:', event.error)
}
}, true); // 使用捕获模式
4. 组合式 API 中的局部捕获 (errorCaptured)
如果你不想让某个子组件的崩溃导致整个页面挂掉,可以使用 errorCaptured 钩子(类似于 React 的 Error Boundary)。
<script setup>
import { onErrorCaptured } from 'vue'
// 用于捕获后代组件的错误
onErrorCaptured((err, instance, info) => {
console.error('组件边界捕获错误:', err, info)
// 返回 false 阻止错误继续向上冒泡到 errorHandler
// 返回 true 则继续冒泡
return false
})
</script>
5. 封装一个通用的"安全调用"函数
为了模拟对异步函数的"全局 try/catch",你可以封装一个高阶函数,自动捕获异步错误并防止崩溃。
// utils/safeCall.js
export function safeCall(asyncFn, defaultVal = null) {
return async (...args) => {
try {
return await asyncFn(...args)
} catch (error) {
console.error('异步函数执行出错:', error)
// 防止抛出异常,返回默认值
return defaultVal
}
}
}
// 在组件中使用
import { safeCall } from '@/utils/safeCall'
const riskyApiCall = safeCall(async () => {
const res = await fetch('/api/bad-endpoint')
return res.json()
}, {}); // 如果出错,返回空对象而不是抛出异常
// 调用时无需再写 try/catch
const data = await riskyApiCall()
📌 总结:全方位防护策略
| 错误类型 | 捕获方式 | 适用场景 |
|---|---|---|
| Vue 组件渲染/生命周期 | app.config.errorHandler |
Vue 内部的运行时错误 |
| 未捕获的异步异常 | window.addEventListener('unhandledrejection') |
async/await 未加 try/catch 的错误 |
| JS 运行时/资源加载 | window.addEventListener('error') |
脚本语法错误、图片/CSS/JS 加载失败 |
| 局部组件崩溃 | errorCaptured 钩子 |
防止某个子组件崩溃导致白屏,显示"降级 UI" |
通过组合使用以上 5 种方式,你就可以在 Vue 3 项目中实现一个坚不可摧的"全局 try...catch"机制。