优雅地处理前端错误边界
🤔 为什么需要错误边界?
在前端开发中,一个组件的错误可能会导致整个应用崩溃。想象一下:用户正在填写一个复杂的表单,突然因为某个组件的小错误,整个页面白屏了——这是多么糟糕的用户体验!
错误边界(Error Boundary)就是为了解决这个问题而生的。它可以捕获子组件树中的 JavaScript 错误,记录错误信息,并显示一个备用 UI,而不是让整个应用崩溃。
💡 React 中的错误边界实现
基础实现
💡 重要提示:截至 React 18,错误边界仍然只能通过类组件实现,这是 React 框架的限制。但我们可以结合 Hooks 来使用它!
import React from 'react';
// 错误边界必须使用类组件实现
class ErrorBoundary extends React.Component {
state = { hasError: false, error: null, errorInfo: null };
// 静态方法,用于在错误发生后更新状态
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
// 捕获错误并记录
componentDidCatch(error, errorInfo) {
console.error('错误边界捕获到错误:', error, errorInfo);
this.setState({ errorInfo });
// 可以在这里上报错误日志
}
// 重置错误状态的方法
handleReset = () => {
this.setState({ hasError: false, error: null, errorInfo: null });
if (this.props.onReset) {
this.props.onReset();
}
};
render() {
if (this.state.hasError) {
// 支持自定义错误 UI
return this.props.fallbackUI || (
<div className="error-boundary">
<h2>抱歉,页面出现了错误 😢</h2>
<div className="error-details">
<p>{this.state.error?.toString()}</p>
<details>
<summary>错误详情</summary>
{this.state.errorInfo?.componentStack}
</details>
</div>
<button
onClick={this.handleReset}
className="retry-button"
>
重试
</button>
</div>
);
}
return this.props.children;
}
}
export default ErrorBoundary;
Hooks + 错误边界的组合使用
虽然错误边界本身需要类组件,但我们可以在 Hooks 组件中轻松使用它:
import React, { useState } from 'react';
import ErrorBoundary from './ErrorBoundary';
// 一个可能出错的 Hooks 组件
const BuggyComponent = () => {
const [count, setCount] = useState(0);
if (count > 3) {
// 模拟错误
throw new Error('计数超过限制了!');
}
return (
<div>
<h3>当前计数: {count}</h3>
<button onClick={() => setCount(count + 1)}>增加计数</button>
</div>
);
};
// 在主组件中使用错误边界包裹
const App = () => {
return (
<div className="app">
<h1>React 错误边界示例</h1>
<ErrorBoundary
// 可以自定义错误 UI
fallbackUI={
<div className="custom-error">
<h3>😱 组件出错了!</h3>
<p>计数组件加载失败,请重试</p>
</div>
}
>
<BuggyComponent />
</ErrorBoundary>
</div>
);
};
export default App;
🚀 进阶优化
1. 错误日志上报
结合现代 JavaScript 语法和 Fetch API:
componentDidCatch(error, errorInfo) {
// 上报错误信息到服务器
fetch('/api/error-report', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
error: error.toString(),
stack: errorInfo.componentStack,
userAgent: navigator.userAgent,
url: window.location.href,
timestamp: new Date().toISOString()
})
})
.catch(reportError => {
console.error('错误上报失败:', reportError);
});
}
2. 支持重置回调
在上面的基础实现中已经包含了重置功能,使用方式:
<ErrorBoundary
onReset={() => {
// 重置相关状态或执行清理操作
console.log('错误边界已重置');
}}
>
<BuggyComponent />
</ErrorBoundary>
⚠️ 注意事项
React 错误边界
- 只能捕获子组件树中的错误,不能捕获自身的错误
- 不能捕获异步代码中的错误(如 setTimeout、fetch 回调等)
- 不能捕获事件处理器中的错误(如 onClick 事件处理函数)
- React 16+ 才支持错误边界
- 必须使用类组件实现(React 框架限制)
Vue 3 错误处理
onErrorCaptured钩子只能捕获子组件的错误- 可以捕获模板编译和渲染时的错误
- 默认会向上传播错误,返回
false可以阻止传播 - 对于异步错误,需要使用
try/catch或全局错误处理 - Vue 3 Composition API 提供了更灵活的错误处理方式
🎯 Vue 3 中的错误处理实现(Setup 语法糖)
Vue 3 提供了更现代的 Composition API,结合 <script setup> 语法糖,可以更优雅地实现错误处理:
<template>
<div>
<slot v-if="!hasError"></slot>
<div v-else class="error-boundary">
<h2>抱歉,页面出现了错误 😢</h2>
<div class="error-details" v-if="error">
<p>{{ error.message }}</p>
<details>
<summary>错误详情</summary>
<pre>{{ error.stack }}</pre>
</details>
</div>
<button @click="handleReset" class="retry-button">
重试
</button>
</div>
</div>
</template>
<script setup>
import { ref, onErrorCaptured } from 'vue';
// 定义 props
const props = defineProps({
// 支持自定义错误 UI
fallbackUI: {
type: Object,
default: null
}
});
// 定义事件
const emit = defineEmits(['reset']);
// 错误状态
const hasError = ref(false);
const error = ref(null);
const errorInfo = ref(null);
// 捕获子组件错误的生命周期钩子
onErrorCaptured((err, instance, info) => {
console.error('Vue 3 错误边界捕获到错误:', err, instance, info);
hasError.value = true;
error.value = err;
errorInfo.value = info;
// 可以在这里上报错误日志
reportError(err, info);
// 返回 false 阻止错误继续向上传播
return false;
});
// 错误日志上报函数
const reportError = async (err, info) => {
try {
await fetch('/api/error-report', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
error: err.toString(),
stack: err.stack,
info,
userAgent: navigator.userAgent,
url: window.location.href,
timestamp: new Date().toISOString()
})
});
} catch (reportErr) {
console.error('错误上报失败:', reportErr);
}
};
// 重置错误状态
const handleReset = () => {
hasError.value = false;
error.value = null;
errorInfo.value = null;
emit('reset');
};
</script>
使用示例
<template>
<div class="app">
<h1>Vue 3 错误处理示例</h1>
<ErrorBoundary
@reset="handleBoundaryReset"
>
<BuggyComponent />
</ErrorBoundary>
<!-- 也可以使用自定义错误 UI -->
<ErrorBoundary
:fallback-ui="customFallback"
>
<AnotherBuggyComponent />
</ErrorBoundary>
</div>
</template>
<script setup>
import { h } from 'vue';
import ErrorBoundary from './ErrorBoundary.vue';
import BuggyComponent from './BuggyComponent.vue';
import AnotherBuggyComponent from './AnotherBuggyComponent.vue';
// 自定义错误 UI
const customFallback = h('div', {
class: 'custom-error'
}, [
h('h3', '📊 图表加载失败'),
h('p', '请检查网络连接后重试'),
h('button', {
onClick: () => console.log('重试按钮点击')
}, '重新加载')
]);
// 错误边界重置回调
const handleBoundaryReset = () => {
console.log('错误边界已重置,页面恢复正常');
};
</script>
📝 总结
错误边界是提升前端应用稳定性和用户体验的重要手段。通过合理使用错误边界,我们可以:
- 防止单个组件错误导致整个应用崩溃
- 提供友好的错误提示,引导用户操作
- 收集错误信息,帮助开发者快速定位问题
- 提升应用的专业感和可靠性
希望这个小技巧对你有所帮助!如果你有任何问题或建议,欢迎在评论区留言讨论 🤗
相关资源:
标签: #React #Vue #前端错误处理 #用户体验优化
848

被折叠的 条评论
为什么被折叠?



