小东西快快学快快记,大知识按计划学,不拖延
继续监控内容总结,今天总结的是前端如何监控静态资源的加载情况,并进行数据上报
本文分为3个部分
1、监控静态资源重要性
2、静态资源测速上报
3、静态资源出错上报
静态资源监控重要性
一个页面的加载快慢,最重要的指标就是静态资源的加载速度了吧。你拼了命得对代码进行优化几十ms,结果静态资源一个不爽就给你耗了几百ms。
所以监控页面静态资源加载情况是十分有必要的。让我们更加全面评估页面的健康情况。
当应用静态资源加载总是缓慢或者出错,会进行告警,这时候马上去排查,是 cdn出了问题还是 资源有问题,就可以减少问题影响时间
静态资源缓慢和失败可是会直接影响用户体验和留存的
庆幸有监控的例子
之前我们上线了一个活动,我们自己验证没有问题。过了一会,静态资源告警就来了,原来是中国移动网络下,该cdn加载有问题,导致图片都加载不出来,所以我们才紧急切换cdn又发布了一版。
如果没有告警,我们哪里会知道什么鬼网络下会出现什么鬼异常。
什么中国移动,联通,电信,铁通,长城一堆网络,又不可能一个个去看,一个个看还存在偶然性。
而且说不定下次不是网络问题了,是xxxx偏门手机资源加载出错,那你可没有办法了吧
你想想如果没有资源告警,难道需要等用户来告诉你吗,想多了,人直接划走了。我淘宝买东西出问题都懒得和商家说…
监控什么静态资源
js , css , 图片,字体,video,audio
静态资源测速上报
1基本原理
这里我们会使用 performance.getEntries() ,它可以获取到页面所有的静态资源和接口请求
我们这次是为了处理静态资源,所以可以使用
代码语言:javascript复制performance.getEntriesByType('resource')
来过滤得到页面的静态资源,如js css img 等
对于单个资源,我们能获取到的信息是这些
根据上面的这些信息,我们可以清楚页面的加载情况
但是需要注意的是
使用 performance.getEntries() 获取资源的时候,可能并不能一下子拿到页面所有的资源,因为资源请求也不是一次性加载完的,就像懒加载的图片
所以我们还需要监听资源的动态载入
具体我们会使用 PerformanceObserver 对资源进行监听。
下面我们看一下这个api的使用例子,我们动态插入一个js,看是否能监听到如下
可以看到木有任何问题
兼容性
可以看到这比我们使用了两个api,来看下他们的兼容性
可以看到 PerformanceObserver 比 getEntries 兼容性差一些。ie全线不支持
2上报什么数据
除了我们会上报getEntris 中拿到的这些数据
我们还需要上报
3问题一览
虽然我们现在以这两个api 可以完成我们的功能,但是仍然存在一些问题需要解决
1、IE 对 PerformanceObeserver 完全不支持
那要怎么办,不管ie 了吗
当然不是
我们会使用一个定时器,循环定时3秒去手动获取资源
performance.getEntries()
这时候有个问题,循环 getEntries(),那不是每次循环获取到的资源有重复的吗??
是的,所以我们在其中加入了一个 资源截取标记位,表示当前已经收集的资源的长度
因为getEntries 获取到的资源列表顺序是一直固定的,所以每次我们调用一次getEntries,就可以把当前已经截取到的位置存起来,下次从这个位置后开始截取
比如这样
代码语言:javascript复制let currentResLen = 0;
setInterval(() => {
const entrys = performance.getEntriesByType('resource');
const currentRes = entrys.slice(currentResLen);
currentResLen = currentRes.length;
},300);
2、浏览器保存资源数据有限
浏览器保存的资源信息是有大小限制的,超过一定数量,剩下的资源就算加载,也不会被记录。performance.getEntries() 就获取不到了。
比如在 chrome 中,最大缓冲资源数是 250个,超过这个数目后,资源无法被记录。
比如我加载了500张图片
使用 getEntries 获取资源的时候,只获取到了 250个
所以我们需要及时清除缓冲区
具体做法是
对缓冲区进行监听,当缓冲区满了之后,就清空资源
代码语言:javascript复制performance.onresourcetimingbufferfull = () => {
performance.clearResourceTimings();
};
但是直接清空是有一定风险的,如果其他开发者想在页面想获取资源,发现死活获取不到,又不知道你这里的清空逻辑,就会困扰到别人....
所以这里清空缓存的时候,最好在控制台输出一些信息,用于提示开发者
另外前面我们说的,我们会有一个 资源截取标记位,用于记录已经截取的资源数量长度
这里清空缓存区的时候,标志位也要相应重置
3、无法判断资源成功失败
从 performance.getEntries 获取的资源列表中,无法判断资源是否加载成功失败
我们这部分只负责上报 资源的 加载速度,错误的资源不应该包含在内,所以需要剔除发生错误的资源。
错误的资源,我们会另外处理
现在可以知道的是
发生错误的 script 和 link 标签,并不会出现在在 getEntries 获取的资源列表中
但是 img 错误,仍然会出现在其中,如果不处理,会导致错误图片误报成功的情况
所以这里的处理是,对错误图片缓存起来,使用 getEntries 获取到资源列表的时候,把错误的图片过滤就可以了
怎么拿到错误的图片呢
1、获取已经加载的错误图片(可能在我们获取资源之前已经加载错误了)
2、监听全局错误事件,拿到错误的图片
具体逻辑做法如下
这里的图片处理逻辑 和我另一篇文章一样,大家可以参考一下,图片错误重载
代码语言:javascript复制const ErrImgList = [];
// 监听动态的图片错误
function watchImgError(params) {
window.document.addEventListener(
'error',
(event) => {
const target = event.target
const url = target.src
// 发生错误的图片存起来,因为错误的也会放在 entries 中,用来过滤
if (target.nodeName === 'IMG') {
ErrImgList.push(url);
}
},
true // 设置为 true 表示捕获
);
}
// 处理已经加载错误的图片
function observeStatic() {
document.querySelectorAll('img').forEach((node) => {
const lazySrc =
node.getAttribute('data-src') ||
node.getAttribute('lazy-src');
if (!lazySrc) {
// 加载完成 complete = true,naturalWidth=0 表示图片没高度,那就是加载失败
if (element.complete && element.naturalWidth === 0) {
ErrImgList.push(node.src);
}
}
});
}
function reportResource(params) {
performance.getEntries().forEach((entry) => {
const { name } = entry;
// 错误的图片不上报测速
if (ErrImgList.indexOf(name) > -1) {
return;
}
// 上报资源测速数据
});
}
4什么时候上报数据
1、window.onload 上报
window.onload() 会在页面完全载入(包括静态资源完成)后触发,所以在这里可以上报一波
2、监听资源加载 再逐个上报
前面总体上报了一波之后,动态增加的资源,可以动态单个上报
5基本流程
可能画得不太规范,但是样子大概是这样
静态资源出错上报
上面我们对资源的加载速度数据进行了上报,我们还需要对错误的资源进行上报
因为 速度 和 错误 不是一个维度的数据,所以我们需要分开上报
1基本原理
使用全局监听错误事件,并设置 第三个参数 为 true,表示捕获,可以捕获到 页面中资源加载错误的情况
代码语言:javascript复制window.addEventListener('error',error,true)
拿到的错误信息如下
之前监听图片错误用于剔除资源测速上报,就是插在这里处理的,并不会监听两次。
2上报什么数据
错误上报的数据就不像 加载速度那样那么多,基本是下面三个
什么时候上报
监听错误事件回调内进行上报
3基本流程
代码参考
https://gitee.com/hoholove/study-code-snippet/blob/master/LOGGER/assetSpeed.js
最后
资源上报我们就已经总结完了,总体不难,就大概用两个api,但是其中还是会涉及兼容和优化的处理细节,总体来说难度不大
鉴于本人能力有限,难免会有疏漏错误的地方,请大家多多包涵, 如果有任何描述不当的地方,欢迎后台联系本人,领取红包