Vue 3 全局错误处理详解与示例

25 阅读3分钟

本文展示一个完整的 Vue 3 全局错误处理模块,用于捕获和处理应用中的各种错误。让我详细解释每个部分并提供实际示例。

一、模块结构解析

1.1 模块导览

// 文件结构
utils/sys/error-handle.ts
├── vueErrorHandler()        // Vue 组件错误处理
├── scriptErrorHandler()     // JavaScript 全局错误处理
├── registerPromiseErrorHandler()  // Promise 错误处理
├── registerResourceErrorHandler() // 资源加载错误处理
└── setupErrorHandle()       // 安装所有错误处理器

二、各功能详解与示例

2.1 Vue 运行时错误处理 (vueErrorHandler)

作用:捕获 Vue 组件中的错误,包括:

  • 组件渲染错误
  • 生命周期钩子错误
  • 事件处理器错误
  • 计算属性错误
<!-- 示例:故意制造错误的组件 -->
<template>
  <div>
    <!-- 1. 渲染错误 -->
    <div>{{ undefinedVariable }}</div> <!-- 访问未定义的变量 -->
    
    <!-- 2. 方法调用错误 -->
    <button @click="throwError">点击抛出错误</button>
    
    <!-- 3. 计算属性错误 -->
    <p>{{ computedError }}</p>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue'

// 4. 生命周期错误
onMounted(() => {
  // 故意抛出错误
  throw new Error('生命周期钩子错误!')
})

const throwError = () => {
  // 5. 事件处理器错误
  throw new Error('按钮点击错误!')
}

const computedError = computed(() => {
  // 6. 计算属性错误
  const obj = null
  return obj.someProperty // TypeError: Cannot read property...
})
</script>

错误捕获流程

  1. Vue 组件发生错误
  2. vueErrorHandler(err, instance, info)被调用
  3. err:错误对象
  4. instance:触发错误的 Vue 组件实例
  5. info:错误发生的位置信息(如 "render function")

2.2 全局脚本错误处理 (scriptErrorHandler)

作用:捕获 JavaScript 运行时错误和语法错误

// 示例 1:同步错误
function causeSyncError() {
  // 访问不存在的属性
  const obj = null
  console.log(obj.property) // TypeError: Cannot read property...
  
  // 调用不存在的函数
  undefinedFunction() // ReferenceError: undefinedFunction is not defined
  
  // 语法错误(在开发阶段就会报错)
  // console.log('hello'  // 缺少右括号
}

// 示例 2:异步错误
setTimeout(() => {
  throw new Error('setTimeout 中的错误')
}, 1000)

// 示例 3:跨域脚本错误(只能获取部分信息)
// <script src="https://other-domain.com/error.js"></script>
// 如果 other-domain.com 的脚本出错,只能获得 "Script error."

参数说明

window.onerror = function(
  message,    // 错误信息
  source,     // 发生错误的脚本URL
  lineno,     // 行号
  colno,      // 列号
  error       // Error对象
) {
  return true // 返回true阻止默认控制台输出
}

2.3 Promise 未捕获错误处理 (registerPromiseErrorHandler)

作用:捕获未处理的 Promise 拒绝(rejection)

// 示例 1:未处理的 Promise 拒绝
const brokenPromise = new Promise((resolve, reject) => {
  reject(new Error('Promise 被拒绝!'))
})
// 没有 .catch() 处理,错误会被捕获

// 示例 2:异步操作中的错误
async function fetchData() {
  const response = await fetch('/api/data')
  if (!response.ok) {
    throw new Error('API 请求失败')
  }
  return response.json()
}

// 调用但未处理错误
fetchData() // 错误会被 unhandledrejection 捕获

// 示例 3:链式调用中丢失的错误
Promise.resolve()
  .then(() => {
    throw new Error('链式调用中的错误')
  })
  // 如果这里没有 .catch(),错误会被捕获

2.4 资源加载错误处理 (registerResourceErrorHandler)

作用:捕获静态资源加载失败

<!-- 示例:资源加载错误 -->
<!DOCTYPE html>
<html>
<body>
  <!-- 1. 图片加载失败 -->
  <img src="non-existent-image.jpg" alt="不存在的图片">
  
  <!-- 2. CSS 文件加载失败 -->
  <link rel="stylesheet" href="non-existent-style.css">
  
  <!-- 3. JavaScript 文件加载失败 -->
  <script src="non-existent-script.js"></script>
  
  <!-- 4. 字体文件加载失败 -->
  <style>
    @font-face {
      font-family: 'MyFont';
      src: url('non-existent-font.woff2') format('woff2');
    }
  </style>
</body>
</html>

捕获的事件详情

window.addEventListener('error', (event) => {
  const target = event.target
  
  if (target.tagName === 'IMG') {
    console.log('图片加载失败:', target.src)
    // 可以设置默认图片
    target.src = '/default-image.jpg'
  }
  
  if (target.tagName === 'SCRIPT') {
    console.log('脚本加载失败:', target.src)
    // 可以加载备用脚本
  }
  
  if (target.tagName === 'LINK' && target.rel === 'stylesheet') {
    console.log('样式表加载失败:', target.href)
    // 可以加载备用样式
  }
}, true) // 使用捕获阶段

三、实际应用示例

3.1 完整配置示例

// main.ts - 使用错误处理
import { createApp } from 'vue'
import App from './App.vue'
import { setupErrorHandle } from './utils/sys/error-handle'

const app = createApp(App)

// 安装错误处理
setupErrorHandle(app)

// 模拟各种错误(用于测试)
if (import.meta.env.DEV) {
  // 测试全局错误
  window.addEventListener('load', () => {
    console.log('错误处理已安装,可以测试以下错误:')
    console.log('1. 访问不存在的变量: window.nonExistent')
    console.log('2. 抛出错误: throw new Error("测试错误")')
    console.log('3. Promise 错误: Promise.reject(new Error("Promise错误"))')
    console.log('4. 资源错误: <img src="error.jpg">')
  })
}

app.mount('#app')

五、总结

这个错误处理模块提供了四个层次的错误捕获:

  1. Vue 组件层:捕获组件内部错误
  2. JavaScript 全局层:捕获所有脚本错误
  3. Promise 层:捕获未处理的 Promise 拒绝
  4. 资源加载层:捕获静态资源加载失败

主要价值

  • 提高应用稳定性
  • 快速定位和修复问题
  • 改善用户体验
  • 便于监控和分析