ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

前端JS错误捕获完整方案,线上异常统一收集

2026/9/2 8:28:05 拓冰建站 浏览量
前端JS错误捕获完整方案,线上异常统一收集 说实话写业务代码的时候我们最怕的不是需求复杂而是代码上线后用户在那边报错我们在这边一脸懵。尤其是移动端用户网络差、手机型号杂有些bug在测试环境死活复现不了一到线上就冒出来。这时候一个健全的前端错误监控体系就特别重要了。今天咱们不聊那些高大上的监控平台就说说最基础的——如何把前端的JS异常、Promise异常、接口异常、甚至是资源加载失败统统“抓”住然后统一上报到服务器。1. 为什么try...catch不够用很多同学说“我写代码都有try...catch啊还能有什么问题”先看这段代码try { // 假设这里有个异步请求 setTimeout(() { throw new Error(接口超时); }, 1000); } catch (e) { console.log(捕获到了, e); // 这里根本不会执行 }setTimeout里的错误try...catch是抓不到的。因为try...catch只能捕获同步代码执行时的错误异步任务执行时外层早就跑完了。所以我们需要一套更底层的全局捕获方案。2. 全局捕获window.onerror 和 window.addEventListenerwindow.onerror可能是最经典的全局错误捕获方式了。window.onerror function(message, source, lineno, colno, error) { console.log(捕获到错误, message); // 这里可以调用上报接口 reportError({ message, source, lineno, colno, stack: error?.stack }); return true; // 阻止浏览器默认行为比如控制台报错 };它基本能捕获到绝大多数同步错误包括一些语法错误如果是外链脚本跨域可能拿不到详细堆栈需要加crossorigin属性。但onerror有个坑它捕获不到 Promise 内部的错误也捕获不到async/await里的异常。new Promise((resolve, reject) { reject(promise出错); }); // window.onerror 收不到这个错误这时候就得请出它的搭档了javascript复制下载// 捕获未处理的 Promise 异常 window.addEventListener(unhandledrejection, function(event) { console.log(未处理的 Promise 错误, event.reason); reportError({ type: unhandledrejection, message: event.reason?.message || String(event.reason), stack: event.reason?.stack }); event.preventDefault(); });unhandledrejection专门抓那些没有被.catch()接住的 Promise 异常。有了它async/await里的错误也跑不掉了。3. 资源加载失败不要跟 JS 错误混为一谈有时候页面白屏不是代码报错而是图片、CSS、CDN 脚本加载失败了。这种错误onerror是收不到的因为加载失败不会触发 JS 异常而是触发一个ErrorEvent。这时候要用另一个监听window.addEventListener(error, function(event) { // 区分资源错误和 JS 错误 if (event.target ! window) { // 这个错误是某个 DOM 元素发出的 const target event.target; const tagName target.tagName; const src target.src || target.href; console.log(资源加载失败, tagName, src); reportError({ type: resourceError, tagName, src }); } }, true); // 注意这里要用捕获阶段为啥要用捕获阶段因为资源加载错误不会冒泡只能在捕获阶段拿到。4. Vue 和 React 框架里的错误如果你用的是 Vue2Vue 内部发生的错误onerror是抓不到的得用 Vue 提供的错误处理器Vue.config.errorHandler function(err, vm, info) { console.log(Vue 错误, err, info); reportError({ type: vueError, message: err.message, stack: err.stack, info }); };Vue3 也是类似的const app createApp(App); app.config.errorHandler (err, vm, info) { reportError({ type: vueError, err, info }); };React 的话官方推荐用Error Boundaries错误边界。虽然它不捕获事件处理器和异步代码但能兜住组件渲染时的错误防止整页白屏。class ErrorBoundary extends React.Component { state { hasError: false }; static getDerivedStateFromError(error) { return { hasError: true }; } componentDidCatch(error, errorInfo) { reportError({ type: reactError, error, componentStack: errorInfo.componentStack }); } render() { if (this.state.hasError) { return h1页面出错了请刷新/h1; } return this.props.children; } }5. 接口请求异常的统一拦截现在项目基本都用 Axios可以在拦截器里统一处理接口报错免得每个请求都写一遍catch。// 请求拦截器 axios.interceptors.response.use( response response, error { // 这里能拿到接口返回的状态码、错误信息 const { status, data } error.response || {}; reportError({ type: apiError, url: error.config?.url, status, message: data?.message || error.message }); return Promise.reject(error); } );这样不管是 404、500还是后端返回的业务错误码都能统一收集。而且还能顺便做一下全局的 loading 关闭、错误提示等。6. 别让上报影响用户体验所有异常都捕获到了但上报也是有讲究的。如果每次报错都发一个请求万一页面瞬间报了几十个错服务器压力就上来了。我这里常用的策略是批量上报节流let errorQueue []; let timer null; function reportError(data) { errorQueue.push(data); if (timer) return; timer setTimeout(() { if (errorQueue.length 0) return; // 用 sendBeacon 或 fetch 批量上报 navigator.sendBeacon(/api/log/errors, JSON.stringify(errorQueue)); errorQueue []; timer null; }, 3000); // 3 秒上报一次 }sendBeacon的好处是即便页面关闭了它也能把数据发出去不会因为页面卸载而中断。7. sourcemap让堆栈不再是天书线上代码都是压缩混淆过的报错堆栈是一堆a、b、c根本看不出是哪个文件哪一行。解决方案就是上传 sourcemap 到监控平台比如 Sentry、Fundebug或者在服务端用source-map库还原堆栈。这块工作量不小但一旦做了排查线上问题的效率能翻好几倍。8. 兜底方案别忘了 window.onerror 的最后一搏不管上面做了多少层捕获总有一些意外情况。我一般会在所有捕获的最外层再挂一个window.onerror作为“最后一道防线”用来抓那些漏网之鱼。// 终极兜底 window.onerror function(msg, url, line, col, error) { // 如果已经通过其他方式上报过了这里可以去重 // 简单起见直接上报 reportError({ type: fatal, msg, url, line, col, stack: error?.stack }); return true; };总结一下这套方案的层级捕获方式适用场景window.onerror同步 JS 错误兜底unhandledrejection未捕获的 Promise 异常addEventListener(error, ..., true)资源加载失败图片、脚本、CSSVue.config.errorHandler/ React Error Boundaries框架层渲染错误Axios 拦截器接口请求异常sendBeacon批量上报性能优化不阻塞用户操作这套组合拳打下来不敢说 100% 覆盖但 95% 以上的线上异常都能抓到。最后想说的是错误监控不是为了“抓”而抓而是为了更快的发现问题和修复 bug。监控数据收集上来之后定期复盘把高频错误消灭掉比加一堆新功能更有价值。好了以上就是我在项目里落地的一套前端异常收集方案。如果你也在折腾这块欢迎在评论区交流踩坑经验。觉得有用的话点个收藏万一哪天用得上呢~