这是我参与「第五届青训营 」伴学笔记创作活动的第 11 天
背景介绍
- 前端监控就是尽可能的采集这一过程以及后续用户交互中产出的性能指标与发生的异常事件并上报到平台完成消费。 为什么需要前端监控?
- 用户对页面质量(打开速度、交互质量等)不满意找到对应的错误归因。
- 前端监控通过对页面数据的采集和上报,来帮助开发者更快速地对质量差的页面进行分析与归因。
常用性能指标
- FP (First Paint) :首次渲染的时间点。FP 时间点之前,用户看到的都是没有任何内容的白色屏幕。
- FCP (First Contentful Paint) :首次有内容渲染的时点。
- FMP (First Meaningful Paint) :首次绘制有意义内容的时间点。
- TTI (Time to Interactive) : 测量页面从开始加载到主要子资源完成渲染,并能够快速、可靠地响应用户输入所需的时间。
- TTI反映页面可用性的重要指标。TTI 值越小,代表用户可以更早地操作页面,用户体验就更好。
- SI (Speed Index) : 衡量页面可视区域加载速度,帮助检测页面的加载体验差异。例如下图,A和B的首次内容出现和完全加载时间是一样的,但是从用户角度A的体验明显更好。
- FID (First Input Delay) :测量从用户第一次与页面交互(比如当他们单击链接、点按按钮等等)直到浏览器对交互作出响应,实际能够开始处理事件时处理程序所经过的时间
- LCP (Largest Contentful Paint) :最大的内容在可视区域内变得可见的时间点
- LCP优点: 1.用户容易理解2.给出与FMP相似的结果3.容易计算和上报
- TBT (Total Blocking Time) :量化主线程在空闲之前的繁忙程度,有助于理解在加载期间,页面无法响应用户输入的时间有多久。
一个页面的TBT,是从FCP到TTI之间(从渲染到交互所用的时间)所有长任务的阻塞时间的总和。
- CLS (Cumulative L ayout Shift) : 量化了在页面加载期间,视口中元素的移动程度。
- 当我们点击按钮时,突然出现了一块内容。无论是以一种增加意外点击几率的方式加载广告,还是在加载图片时文本向下移动,内容的意外移动都会让人非常不舒服。
前端常见异常
- 静态资源错误
- 静态资源:加载页面所需的html、css和js等文件,以及其他各类多媒体文件,如图片、音频和视频等。
- 静态资源错误:在拉取和加载静态资源的过程中发生了预期之外的错误,如网络异常等,导致静态资源无法正常渲染到页面上。
- 请求异常
请求成功率=请求成功数/(请求成功数+请求失败数)
- JS错误
- 在页面运行时发生的Js错误会严重影响页面的正常渲染与交互,是前端监控的重点。
- 白屏异常
- 前面几类异常都可以通过浏览器提供的标准化方法来监听到,而白屏异常没有标准化的监听方法,所以更考验前端监控开发者的功底。
- 通常我们可以通过判断DOM树的结构来粗略的判断白屏是否发生。
- 监听到白屏发生后,我们还需要对白屏的发生进行归因。
- 通常导致白屏发生的原因可能有如下几点:
- 发生Js错误导致关键资源渲染失败。
- 请求异常或静态资源加载失败。
- 长时间的Js线程繁忙阻塞渲染任务。
实操
1. 性能指标的监控
performanceObserver
/**
* 列举出性能指标对应的 entry type
* fp,fcp --> paint
* lcp --> largest-contentful-paint
* fip --> first-input
*/
const entryTypes = ['paint', 'largest-contentful-paint', 'first-input']
// 1. 通过 PerformanceObserver 监听
const p = new PerformanceObserver(list => {
for (const entry of list.getEntries()) {
console.log(entry);
}
})
p.observe({ entryTypes });
performance
// 2. 也可以通过 window.performance 对象拿到 fp fcp 和 fip。
// 注意如果同步打印他们是取不到值的,想想为什么?
window.performance.getEntriesByType('paint');
window.performance.getEntriesByType('first-input');
封装monitor
- 1.起名字
- 2.监听能力
- 3.主动开启,而不是被动开启
- 4.上报能力
// 3. 封装成一个 monitor
function createPerfMonitor(report: ({ name: string, data: any }) => void) {
const name = 'performance';
const entryTypes = ['paint', 'largest-contentful-paint', 'first-input']
function start() {
const p = new PerformanceObserver(list => {
for (const entry of list.getEntries()) {
report({ name, data: entry });
}
})
p.observe({ entryTypes });
}
return { name, start }
}
2. JS错误
// 1. 监听 js 执行报错
window.addEventListener("error", (e) => {
// 只有 error 属性不为空的 ErrorEvent 才是一个合法的 js 错误
if (e.error) {
console.log('caputure an error', e.error);
}
});
// throw(new Error('test'));
// 2. 监听 promise rejection
window.addEventListener("unhandledrejection", (e) => {
console.log('capture a unrejection', e);
});
Promise.reject('test');
// 3. 封装成一个 monitor
function createJsErrorMonitor(report: ({ name: string, data: any }) => void) {
const name = "js-error";
function start() {
window.addEventListener("error", (e) => {
// 只有 error 属性不为空的 ErrorEvent 才是一个合法的 js 错误
if (e.error) {
report({ name, data: { type: e.type, message: e.message } });
}
});
window.addEventListener("unhandledrejection", (e) => {
report({ name, data: { type: e.type, reason: e.reason } });
});
}
return { name, start }
3. 静态资源错误
- 被划掉的是已经不使用的,但对于一些老版本的用户会用到
- true是指在捕获阶段使用,否则默认false在冒泡阶段使用 (代码待补充)
4. 请求异常
总结
前端监控整个流程