1. 为什么需要全局捕获资源加载错误在前端开发中图片、脚本等静态资源加载失败是常见但容易被忽视的问题。当用户网络不稳定或CDN节点异常时页面可能静默地丢失关键内容。我曾遇到一个电商项目促销活动期间因第三方统计脚本加载失败导致转化率数据完全丢失却无人察觉。传统的错误处理方式是在每个资源上单独添加onerror回调但这种方式存在三个明显缺陷代码重复率高维护成本大动态注入的资源容易遗漏处理无法统计整体错误率通过window.onerror和error事件捕获的组合方案我们可以实现实时监控所有资源加载状态统一错误上报逻辑动态降级处理机制2. 基础实现方案与核心API2.1 window.onerror的基础用法这个全局错误处理器可以捕获运行时异常和脚本加载失败window.onerror function(message, source, lineno, colno, error) { console.log(错误信息:, message) console.log(脚本URL:, source) console.log(行号:, lineno) console.log(列号:, colno) console.log(Error对象:, error) // 返回true阻止默认错误提示 return true }但需要注意三个关键限制跨域脚本只会返回Script error.需设置crossorigin属性无法捕获资源如图片、CSS的加载错误不同浏览器参数顺序可能不一致2.2 增强版的error事件监听通过事件冒泡捕获所有资源错误window.addEventListener(error, function(event) { // 区分资源类型 const tagName event.target.tagName if (tagName IMG) { handleImageError(event.target) } else if (tagName SCRIPT) { reportScriptError(event.target.src) } }, true) // 使用捕获阶段这里必须设置useCapture为true因为资源加载错误不会冒泡。3. 生产环境完整解决方案3.1 错误分类处理策略根据资源类型采取不同降级方案错误类型处理方案示例关键脚本重试加载支付SDK非关键脚本延迟加载统计分析首屏图片替换备用图商品主图装饰图片隐藏元素背景图实现示例function handleResourceError(event) { const target event.target const resourceType target.tagName switch(resourceType) { case IMG: if (isCriticalImage(target)) { target.src getFallbackImage() } else { target.style.display none } break case SCRIPT: if (isCriticalScript(target)) { retryLoading(target, 3) // 最多重试3次 } break } reportToAnalytics({ type: resourceType, url: target.src || target.href, timestamp: Date.now() }) }3.2 性能优化注意事项错误回调中避免同步操作// 错误做法 window.onerror function() { sendBeacon() // 同步XHR会阻塞页面 } // 正确做法 window.onerror function() { setTimeout(() { navigator.sendBeacon(/log, data) }, 0) }使用WeakMap存储重试次数避免内存泄漏const retryMap new WeakMap() function retryLoading(element, max) { const count retryMap.get(element) || 0 if (count max) return retryMap.set(element, count 1) element.src ${element.src.includes(?) ? : ?}_retry${Date.now()} }4. 监控上报与数据分析4.1 错误数据聚合方案建议采用以下维度聚合资源类型img/script/link域名分布区分自有和第三方地理区域通过IP解析浏览器版本发生时间function reportError(data) { const payload { ...data, ua: navigator.userAgent, url: window.location.href, resolution: ${window.screen.width}x${window.screen.height}, lang: navigator.language } // 使用sendBeacon保证页面关闭时也能上报 navigator.sendBeacon(/api/error-log, JSON.stringify(payload)) }4.2 报警阈值设置建议根据历史数据设置动态阈值全局错误率突增50%特定CDN域名错误率2%关键资源连续3次加载失败移动端错误率是桌面端的2倍以上5. 特殊场景处理经验5.1 动态注入资源的捕获对于通过createElement动态添加的资源需要特殊处理const originalCreateElement document.createElement.bind(document) document.createElement function(tagName) { const element originalCreateElement(tagName) if (tagName.toLowerCase() img) { element.onerror function() { handleImageError(this) } } return element }5.2 Service Worker中的处理在Service Worker中需要额外监听fetch事件self.addEventListener(fetch, event { event.respondWith( fetch(event.request).catch(err { reportSwError({ url: event.request.url, type: fetch, stack: err.stack }) return caches.match(/fallback) }) ) })5.3 第三方脚本的错误隔离为防止第三方脚本错误影响主流程function loadThirdPartyScript(url) { const iframe document.createElement(iframe) iframe.style.display none document.body.appendChild(iframe) const doc iframe.contentDocument const script doc.createElement(script) script.src url script.onerror () { iframe.remove() console.warn(第三方脚本加载失败: ${url}) } doc.body.appendChild(script) }6. 实际项目中的踩坑记录跨域脚本的详细错误信息 必须同时满足脚本添加crossorigin属性服务器返回Access-Control-Allow-Origin脚本必须严格匹配的MIME类型error事件重复触发 图片加载失败后如果修改src属性会再次触发error需要设置标记位let isHandled false img.onerror function() { if (isHandled) return isHandled true // 处理逻辑 }浏览器兼容性差异IE8/9不支持标准的error事件Safari早期版本对script.error的支持不完整移动端WebView可能有特殊行为性能影响评估 在低端设备上测试发现全局error监听会使页面加载时间增加约15-30ms建议在DOMContentLoaded后再添加非关键监听器。
阅读完成 · 觉得有帮助?