前端错误捕获
前端错误捕获
- 一、怎么做错误捕获
- 1.同步错误捕获
- 2.异步错误捕获
- 3.全局错误捕获
- 4.框架层错误捕获
- 5.资源加载错误
- 二、JS有多少种错误类型
- 1.ECMAScript标准错误
- 2.浏览器特定错误
- 3.自定义错误
- 三、如何做统一的错误处理
- 1. 在项目中实现外层统一错误处理
- 2.错误分级策略
- 3.监控能力
- 四、异常监控工具
- - 常见的监控工具
- - 以React为例,如何使用Sentry进行前端异常监控和处理
- 1.安装依赖
- 2.初始化(入口文件)
- 3. 全局错误边界
- 4.异步操作错误捕获
- 5. 路由追踪
- 6. 自定义用户上下文
一、怎么做错误捕获
直入主题,以下是错误捕获的几种方法
1.同步错误捕获
try…catch
2.异步错误捕获
Promise/catch 与 async/await
- Promise 链:通过 .catch() 处理拒绝状态
- async/await + try…catch:更接近同步写法
3.全局错误捕获
window.onerror 与 unhandledrejection
- ① window.onerror:捕获未被 try…catch 处理的全局错误
window.onerror = (message, source, lineno, colno, error) => {// 记录错误信息(如上报服务器)reportError({message, // 错误的描述 例如:"Uncaught TypeError: Cannot read properties of null (reading 'name')"source, // 发生错误的脚本文件 URLline: lineno, // 错误发生的行号column: colno, // 错误发生的列号stack: error?.stack // 实际的错误对象,包含完整的堆栈信息(stack属性)});// 返回 true 阻止默认事件(如控制台红色错误信息,某些浏览器有效,Chrome不支持)return true;
};
- ② unhandledrejection:捕获未处理的 Promise 拒绝
window.addEventListener('unhandledrejection', event => {const error = event.reason;// 处理未被 .catch() 捕获的 Promise 错误console.error('未处理的 Promise 错误', error);event.preventDefault(); // 阻止控制台默认报错
});
4.框架层错误捕获
① React:通过 ErrorBoundary 组件捕获子组件错误
import { Component } from 'react';class ErrorBoundary extends Component {state = { hasError: false };componentDidCatch(error, info) {// 记录错误reportError(error, info); // 上报错误this.setState({ hasError: true });}render() {if (this.state.hasError) {// 显示错误提示return <div>页面出错啦,请刷新重试</div>;}return this.props.children;}
}
② Vue:通过 errorCaptured 钩子或全局 errorHandler
// 组件内捕获子组件错误
export default {errorCaptured(error, instance, info) {console.error('子组件错误', error, info);return false; // 阻止错误继续向上传播}
};// 全局捕获(main.js)
Vue.config.errorHandler = (error, vm, info) => {reportError(error, { vm, info });// 显示全局错误提示app.$message.error('系统异常,请稍后再试');
};
5.资源加载错误
捕获图片、脚本、样式等资源加载失败:
<img src="/invalid-image.jpg" onerror="this.src='default.jpg'; this.onerror=null" alt="默认图片"
><script src="/invalid.js" onerror="console.error('脚本加载失败')"
></script>
二、JS有多少种错误类型
1.ECMAScript标准错误
① 语法错误 SyntaxError:如缺少括号,关键字写错等
② 引用未定义变量 ReferenceError
③ 类型错误 TypeError:如对非函数的调用,属性不存在等
④ 超出范围错误 RangeError:数值长度超出范围,栈溢出
⑤ URI编码解码错误 URIError:decodeURI(‘%2’);
⑥ eval()执行错误 EvalError(逐渐废弃)
2.浏览器特定错误
① 网络请求失败 NetworkError:如 404、500 状态码
② DOM操作错误 DOMException:如删除非子节点
③ 安全策略阻止操作 SecurityError:如跨域访问
3.自定义错误
继承 Error 基类创建业务专属错误:
class NetworkError extends Error {constructor(message, statusCode) {super(message);this.statusCode = statusCode;this.name = 'NetworkError';}
}// 使用
try {if (res.status >= 400) {throw new NetworkError('请求失败', res.status);}
} catch (error) {if (error instanceof NetworkError) {showToast(`网络错误 ${error.statusCode}`);}
}
三、如何做统一的错误处理
1. 在项目中实现外层统一错误处理
需结合全局捕获、错误分类、日志上报,以及用户友好提示
- 通过window.onrror和监听unhandledrejection事件实现全局错误捕获
- 在用户提示层面,区分错误类型,返回不同提示
- 生产环境上,做错误日志上报
2.错误分级策略
- 致命 Fatal(导致页面崩溃|核心功能不可用):立即报警,优先修复。例如白屏。
- 错误 Error(影响部分功能,但页面可使用):24小时内修复。例如表单提交失败。
- 警告 Warning(非阻塞性问题,可能引发潜在风险):版本迭代中优化。
- 信息 Info(正常流程中的异常分支):定期分析,优化逻辑
3.监控能力
- 错误捕获:运行时错误、资源加载错误、promise拒绝、异步错误
- 上下文采集:用户环境(浏览器、系统、版本),影响范围(URL、路由记录),用户行为(点击、输入)
- 错误分析:错误分组(按类型)、影响范围(受影响的用户、触发频率),趋势统计
- 报警机制:邮件通知等
四、异常监控工具
- 常见的监控工具
Sentry:支持全平台监控、错误分组聚合、堆栈追踪、用户行为关联,生态丰富,适用中大型项目、复杂前端应用(免费版 + 企业付费版)
Bugsnag:轻量级、高可靠性,专注错误监控,提供详细的错误影响分析,适用对性能敏感的项目(免费版 + 付费版)
Fundebug:中文支持好,集成简单,提供可视化错误重现功能,适用中小型项目、快速接入需求(免费版 + 付费版)
- 以React为例,如何使用Sentry进行前端异常监控和处理
1.安装依赖
npm install @sentry/react @sentry/tracing
2.初始化(入口文件)
// index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import * as Sentry from '@sentry/react';
import { BrowserTracing } from '@sentry/tracing';// 初始化 Sentry
Sentry.init({// 基础配置dsn: 'https://your-dsn@sentry.io/123456', // Sentry 项目的唯一标识符,类似于 API Keyintegrations: [new BrowserTracing({// 与 React Router 集成的核心逻辑,用于追踪路由变化routingInstrumentation: Sentry.reactRouterV6Instrumentation(React.useEffect, // React 生命周期钩子,监听路由变化的触发器useLocation, //获取当前 URL 路径useNavigationType, // 区分导航类型(如用户点击链接 vs 代码跳转)startTransaction // 创建 Sentry 性能事务)})],// 高级配置beforeSend: (event) => {// 错误发送前的回调函数,过滤特定错误if (event.message?.includes('Network request failed')) {return null; // 不上报网络请求失败}return event;// 过滤第三方脚本错误if (event.stacktrace && event.stacktrace.frames) {const isThirdParty = event.stacktrace.frames.some(frame => frame.filename && frame.filename.includes('node_modules'));if (isThirdParty) return null;}},attachStacktrace: true, // 强制附加堆栈信息,帮助定位错误发生的精确位置normalizeDepth: 5, // 堆栈追踪深度,避免深层嵌套对象导致的性能问题release: 'my-app@1.0.0', // 指定当前应用版本sampleRate: 0.8 // 错误采样率(控制错误上报量)tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.2 : 1.0, // 采样率(0-1),生产环境建议0.2environment: process.env.NODE_ENV // 区分开发/生产环境
});const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<React.StrictMode><App /></React.StrictMode>
);
<React.StrictMode>是开发阶段的问题检测工具,主要负责检查子树内组件的逻辑问题,仅在开发环境有效
3. 全局错误边界
// ErrorBoundary.js
import React, { Component } from 'react';
import * as Sentry from '@sentry/react';class ErrorBoundary extends Component {state = { hasError: false };static getDerivedStateFromError() {return { hasError: true };}componentDidCatch(error, errorInfo) {// 上报错误到 SentrySentry.captureException(error, { extra: errorInfo });console.error('ErrorBoundary caught an error', error, errorInfo);}render() {if (this.state.hasError) {// 显示降级 UIreturn <div>页面加载失败,请刷新重试</div>;}return this.props.children;}
}export default ErrorBoundary;
ErrorBoundary是运行时的错误捕获机制,捕获其子树内的运行时错误,在开发 / 生产环境均生效,错误发生时会渲染备用 UI,防止组件错误导致页面崩溃,展示友好提示,仅捕获渲染同步错误,不捕获异步错误(setTimeout、Promise、fetch),所以需要下面的异步错误捕获
官方定义是:ErrorBoundary仅捕获渲染期间、生命周期方法和构造函数中的错误。但需要注意的是异步请求的响应处理错误,若在渲染时抛出则捕获,若在回调中抛出则不捕获
4.异步操作错误捕获
// 使用 useErrorBoundary 钩子捕获异步错误
import { useErrorBoundary } from '@sentry/react';function MyComponent() {const { showBoundary } = useErrorBoundary();const fetchData = async () => {try {const response = await fetch('/api/data');const data = await response.json();return data;} catch (error) {// 捕获异步错误并触发错误边界showBoundary(error);}};return <button onClick={fetchData}>加载数据</button>;
}
5. 路由追踪
// 增强路由错误监控
import {startTransaction,useLocation,useNavigationType,
} from '@sentry/react';
import {createRoutesFromElements,RouterProvider,useRoutes,
} from 'react-router-dom';// 包裹路由配置
const routes = createRoutesFromElements(<Route path="/" element={<App />}><Route path="dashboard" element={<Dashboard />} /><Route path="profile" element={<Profile />} /></Route>
);// 使用 Sentry 包装路由
const SentryRoutes = () => {const location = useLocation();const navigationType = useNavigationType();return useRoutes(routes, {onError(error) {Sentry.captureException(error);},beforeRouteRender() {startTransaction({name: 'Route Change',op: 'navigation',metadata: {source: 'route',route: location.pathname,},});},});
};// 在 RouterProvider 中使用
<RouterProvider router={{ routes: <SentryRoutes /> }} />
6. 自定义用户上下文
// 登录后设置用户信息,将错误与具体用户关联
Sentry.setUser({id: user.id,username: user.username,email: user.email
});// 登出时清除用户信息
Sentry.setUser(null);// 面包屑(Breadcrumbs)记录用户操作
Sentry.addBreadcrumb({category: 'ui.click',message: '用户点击提交按钮',data: { formFields: ['username', 'email'] },level: 'info'
});// 手动监控函数性能
const transaction = Sentry.startTransaction({name: 'fetchUserData',op: 'network'
});fetch('/api/user').then(res => res.json()).then(data => {transaction.finish(); // 结束事务}).catch(error => {transaction.setStatus('error');transaction.finish();Sentry.captureException(error);});