静态资源加载失败是前端监控里很容易被忽视的一环。一个图片挂了,页面布局未必会崩,用户可能只是看到一个小裂图,甚至根本不会主动反馈。但这类问题积累多了,会直接影响用户体验和功能完整性。有没有办法在用户察觉之前,就主动把这些加载失败的资源揪出来?Resource Timing API提供了一种相对轻量的思路,配合jQuery的便捷操作,可以快速实现一套资源加载失败检测与告警的小工具。

先搞清楚Resource Timing API到底给了我们什么
Resource Timing API是浏览器Performance API家族的一员,专门用来记录页面中所有资源(图片、脚本、样式表、XHR请求等)的网络时序和大小信息。通过performance.getEntriesByType('resource'),可以拿到一个资源条目数组,每个条目都包含name(资源URL)、initiatorType(资源类型,如img、script、css)、duration(从开始到结束的总耗时)、transferSize(传输大小,包含响应头)、decodedBodySize(解码后的体积)等字段。
这里有一个关键的认知:Resource Timing API本身并不会直接告诉你某个资源“加载失败”了。它只是如实记录每个资源在时间线上的表现。一个加载失败的资源,在条目中往往表现为duration为0、transferSize为0、decodedBodySize为0,因为请求可能根本没有到达服务器,或者被浏览器直接中断。但要注意,资源本身如果是从缓存中读取的(比如强缓存命中的图片),transferSize也可能是0,这时候就需要结合duration和initiatorType进一步判断。另外,对于跨域资源,如果服务器没有返回Timing-Allow-Origin响应头,浏览器会把transferSize、decodedBodySize等敏感字段置为0,这就会造成误判,处理时需要特别小心。
还有一种情况是资源返回了HTTP状态码404或500,但浏览器端拿不到状态码信息(除非使用较新的responseStatus属性,但兼容性还不理想)。因此,大多数实践方案还是基于“零值”特征来推断加载失败,再结合其他信号做二次校验。
用jQuery实现资源加载失败检测与告警
下面这段代码演示了如何用jQuery来遍历Resource Timing条目,筛选出疑似加载失败的资源,并把结果通过一个简单的POST请求发送到告警接口。代码中使用了$.grep进行过滤,用$.each处理结果,用$.post完成告警上报。
过滤条件的设计很关键:资源条目的duration为0,且transferSize为0,且decodedBodySize为0。同时排除name以data:或blob:开头的内部资源,因为这些资源不属于网络请求,出现零值是正常的。如果资源是跨域的,而且timing-allow-origin没有设置,那么transferSize会被强制置零,这时候仅凭零值判断很可能误报,所以可以额外检查duration是否也为0,因为跨域资源虽然无法获取大小信息,但加载耗时通常不为0。如果连耗时都是0,那基本可以断定这个请求根本没发生或者被立即拒绝了。
(function() {
// 等待页面加载完成,确保Resource Timing条目已经生成
$(window).on('load', function() {
// 获取所有资源条目
var resources = performance.getEntriesByType('resource');
// 过滤出疑似加载失败的资源
var failedResources = $.grep(resources, function(entry) {
// 排除data:和blob:等非网络资源
if (entry.name.indexOf('data:') === 0 || entry.name.indexOf('blob:') === 0) {
return false;
}
// 核心判断:耗时、传输大小、解码大小全部为0
return entry.duration === 0 && entry.transferSize === 0 && entry.decodedBodySize === 0;
});
if (failedResources.length > 0) {
// 提取资源URL和类型信息用于告警
var alertData = $.map(failedResources, function(entry) {
return {
url: entry.name,
type: entry.initiatorType
};
});
// 使用jQuery发送告警请求
$.post('/api/resource-alert', { failures: JSON.stringify(alertData) })
.done(function() {
console.log('静态资源加载失败告警已上报');
})
.fail(function() {
console.warn('告警上报失败,请检查接口可用性');
});
}
});
})();
这段代码可以直接放到页面底部或者绑定在load事件上执行。需要注意的是,performance.getEntriesByType('resource')返回的条目数量在大型单页应用中可能非常多,如果页面资源很多,可以加一个去重逻辑,因为同一个资源在SPA路由切换时可能会被多次记录,重复上报会造成告警噪音。
真实生产环境中,建议把检测逻辑封装成一个独立的函数,并加入节流或防抖,避免频繁触发。另外,告警接口的URL需要根据实际后端结构调整,这里只是示例。
这些坑必须先填上,否则告警会变成狼来了
第一个坑是跨域资源。前面已经提到,如果静态资源部署在CDN或其他域名下,且响应头中没有Timing-Allow-Origin,那么浏览器会出于安全考虑隐藏transferSize和decodedBodySize。这种情况下,一个正常加载的跨域图片也可能被误判为失败。解决办法有两个:一是在CDN或资源服务器上添加Timing-Allow-Origin: *响应头(或指定源);二是在前端判断时,如果发现资源是跨域的,就只依赖duration是否大于0来判断,但这样会漏掉一些只有大小信息缺失的情况。更稳妥的做法是结合error事件来做交叉验证。
第二个坑是缓存命中。浏览器对强缓存命中的资源,transferSize会是0,但duration通常不为0(因为从缓存读取也要耗费极小的时间)。所以我们的判断条件中同时要求duration为0,可以有效避开缓存资源。但要注意,某些浏览器在极端情况下缓存读取的duration也可能为0(极短时间的舍入),如果遇到过这种情况,可以在过滤逻辑中额外检查entry.decodedBodySize是否大于0,如果大于0则说明内容确实解码成功,不算失败。
第三个坑是异步加载的资源和动态插入的资源。如果你用jQuery动态创建了一个<img>标签并设置src,这个资源的加载时序也会被Resource Timing记录。但如果你在load事件之后才动态插入资源,那么window.onload早已触发,你需要手动再次调用检测函数。因此建议把检测逻辑抽离出来,在合适的时机(比如SPA路由切换后、Ajax完成渲染后)主动执行一次。
从告警到定位问题,还能再进一步
单纯收集失败的资源URL还不够,为了快速定位问题,应该在告警数据中带上更多上下文信息。例如页面URL、用户代理、发生时间,甚至可以尝试通过jQuery去探测该资源是否真的不可用(比如用$.ajax发一个HEAD请求,但会受跨域限制)。更务实的做法是,在告警信息中附带资源的initiatorType,这样就能知道失败的是图片、脚本还是样式表,排障时方向更明确。
另外,Resource Timing API还提供了responseStatus属性(Chrome 109+等较新版本支持),如果浏览器支持,可以直接通过该字段判断HTTP状态码是否为4xx或5xx,这样检测准确率会大幅提高。可以在代码中做能力检测,优先使用responseStatus,不支持时再回退到零值判断。下面是一个简单的增强版过滤示例。
function isLikelyFailed(entry) {
// 优先使用responseStatus字段(浏览器支持时)
if (typeof entry.responseStatus !== 'undefined') {
return entry.responseStatus >= 400;
}
// 回退方案:零值判断
if (entry.name.indexOf('data:') === 0 || entry.name.indexOf('blob:') === 0) {
return false;
}
return entry.duration === 0 && entry.transferSize === 0 && entry.decodedBodySize === 0;
}
$(window).on('load', function() {
var resources = performance.getEntriesByType('resource');
var failed = $.grep(resources, isLikelyFailed);
if (failed.length) {
var reportData = $.map(failed, function(entry) {
return {
url: entry.name,
type: entry.initiatorType,
status: entry.responseStatus || 'unknown'
};
});
$.post('/api/resource-alert', { failures: JSON.stringify(reportData) });
}
});
这段代码优先检测responseStatus字段,明确判断HTTP状态码是否大于等于400,这样即使资源跨域且没有暴露大小信息,也能准确识别失败。对于不支持该字段的浏览器,自动回退到零值判断逻辑,兼顾了准确性和兼容性。
总的来说,用jQuery配合Resource Timing API做静态资源加载失败告警,成本很低,但能补上前端监控中缺失的一块。关键是要理解API的数据特征,避开跨域和缓存的陷阱,并根据实际场景调整过滤条件。把检测逻辑和上报动作解耦,再借助jQuery的简洁语法,就能快速落地一套可用的告警机制。
jQueryResource Timing API静态资源加载失败修改时间:2026-10-06 23:39:08