如何捕获组件的异常情况
文章目录
- 一、通用异常捕获(全局兜底)
- 1. 捕获同步 / 宏任务异常
- 2. 捕获 Promise 未处理异常
- 二、Vue 组件异常捕获
- 1. 组件内局部捕获(errorCaptured 钩子)
- 2. 全局捕获(app.config.errorHandler)
- 三、React 组件异常捕获
- 1. 定义错误边界组件
- 2. 使用错误边界包裹组件
- 3. 捕获 React 18+ 并发模式异常
- 四、特殊场景处理
- 总结
在前端开发中,捕获组件异常(包括崩溃情况)是保障应用稳定性的关键。以下是针对不同框架通用的异常捕获方案,以及针对 Vue、React 的的具体实现:
一、通用异常捕获(全局兜底)
无论使用什么框架,都可以通过全局事件监听捕获未被局部处理的异常,作为最后一道防线:
1. 捕获同步 / 宏任务异常
// 监听全局未捕获的同步异常和宏任务异常(如 setTimeout 中抛出的错误)
window.addEventListener('error', (event) => {const { error, filename, lineno, colno } = event;console.error('全局异常捕获:', {message: error.message,stack: error.stack,file: filename,line: lineno,column: colno});// 上报到监控系统reportToMonitor({type: 'global-error',error: error.message,stack: error.stack,time: Date.now()});// 阻止浏览器默认报错(可选)event.preventDefault();
});
2. 捕获 Promise 未处理异常
// 监听未被 catch 的 Promise 异常(如 fetch 失败未处理)
window.addEventListener('unhandledrejection', (event) => {const reason = event.reason;console.error('未处理的 Promise 异常:', reason);reportToMonitor({type: 'unhandled-rejection',error: reason.message || String(reason),stack: reason.stack,time: Date.now()});// 阻止浏览器默认警告event.preventDefault();
});
二、Vue 组件异常捕获
Vue 提供了专门的 API 捕获组件内的异常,包括渲染、生命周期等错误:
1. 组件内局部捕获(errorCaptured 钩子)
用于捕获子组件抛出的异常,可在当前组件内处理或向上传递:
<template><div><ChildComponent /><!-- 异常提示 --><div v-if="hasError" class="error">组件加载失败,请刷新重试</div></div>
</template><script>
import ChildComponent from './ChildComponent.vue';export default {components: { ChildComponent },data() {return { hasError: false };},// 捕获子组件异常errorCaptured(err, vm, info) {console.error('子组件异常:', err, '组件:', vm, '信息:', info);this.hasError = true; // 显示错误提示reportToMonitor({type: 'vue-component-error',error: err.message,component: vm.$options.name,info: info // 错误发生的阶段(如 "render"、"lifecycle-hook:mounted")});// 返回 false 阻止异常继续向上传播return false;}
};
</script>
或者
<template><ChildComponent /><div v-if="hasError">子组件加载失败</div>
</template><script setup>
import { ref, onErrorCaptured } from 'vue';
import ChildComponent from './ChildComponent.vue';const hasError = ref(false);// 组合式 API 中捕获子组件异常
onErrorCaptured((err, instance, info) => {console.error('捕获子组件异常:', err);console.log('组件实例:', instance);console.log('错误信息:', info);hasError.value = true;// 返回 false 阻止异常向上传播return false;
});
</script>
2. 全局捕获(app.config.errorHandler)
捕获整个应用内所有组件的异常(包括 Vue 自身的错误):
import { createApp } from 'vue';
import App from './App.vue';const app = createApp(App);// 全局 Vue 异常处理器
app.config.errorHandler = (err, vm, info) => {console.error('全局 Vue 异常:', err, '组件:', vm, '信息:', info);reportToMonitor({type: 'vue-global-error',error: err.message,component: vm?.$options.name || 'unknown',info: info,stack: err.stack});
};app.mount('#app');
三、React 组件异常捕获
React 从 v16 开始提供 Error Boundary(错误边界)组件捕获子组件异常:
1. 定义错误边界组件
import React from 'react';class ErrorBoundary extends React.Component {constructor(props) {super(props);this.state = { hasError: false, error: null };}// 捕获子组件异常static getDerivedStateFromError(error) {// 更新状态,下一次渲染显示错误 UIreturn { hasError: true, error };}// 记录错误信息(如上报)componentDidCatch(error, errorInfo) {console.error('React 组件异常:', error, errorInfo);reportToMonitor({type: 'react-component-error',error: error.message,stack: error.stack,componentStack: errorInfo.componentStack // 组件调用栈});}render() {if (this.state.hasError) {// 自定义错误 UIreturn this.props.fallback || <div>发生错误,请刷新页面</div>;}// 正常渲染子组件return this.props.children;}
}export default ErrorBoundary;
2. 使用错误边界包裹组件
import ErrorBoundary from './ErrorBoundary';
import RiskyComponent from './RiskyComponent'; // 可能崩溃的组件function App() {return (<div>{/* 用错误边界包裹可能出错的组件 */}<ErrorBoundary fallback={<div>子组件加载失败</div>}><RiskyComponent /></ErrorBoundary></div>);
}
3. 捕获 React 18+ 并发模式异常
React 18+ 并发模式异常
对于并发更新中的异常,可结合 useEffect 和全局监听:
// 函数组件内捕获异步更新异常
useEffect(() => {const handleError = (error) => {console.error('并发更新异常:', error);reportToMonitor({ type: 'react-concurrent-error', error: error.message });};window.addEventListener('error', handleError);return () => window.removeEventListener('error', handleError);
}, []);
四、特殊场景处理
异步操作异常:组件内的 setTimeout、fetch、Promise 等异步操作,需在内部用 try/catch 或 .catch() 捕获(全局监听作为兜底)。
SSR 环境异常:在服务端渲染(如 Next.js、Nuxt.js)中,需同时处理服务端和客户端异常:
服务端
:用 try/catch 包裹渲染逻辑。
客户端
:同上述浏览器端方案。
第三方组件异常:对于无法修改源码的第三方组件,用错误边界(React)或 errorCaptured(Vue)包裹,避免其崩溃影响整个应用。
总结
捕获组件异常的核心原则是 “多层防御”:
局部捕获:组件内处理已知可能出错的逻辑(try/catch、组件内钩子)。
层级捕获:通过父组件 / 错误边界捕获子组件异常,隔离错误范围。
全局兜底:用 window.error 和 unhandledrejection 捕获所有未处理异常,确保无遗漏。
同时,异常信息应包含错误消息、调用栈、组件名称、发生时间等,便于排查问题。