前端监控之页面性能监控
为什么要做前端监控?
- 更快发现问题和解决问题
- 做产品的决策依据
- 为业务扩展提供了更多可能性
指标数据监控
- 性能监控:首屏加载时间,卡顿率,http请求的响应时间,静态资源下载时间,页面渲染时间,页面可交互时间…
初始化
- 初始化参数
- 参数校验
性能监控
- 首次绘制时间(FP):
- 监听类型为paint
- 封装成一个promise 当入口名称为first-paint时将时间返回
// 首次绘制,标记浏览器渲染任何在视觉上不同于导航前屏幕内容之内容的时间点
new Promise((resolve, reject) => {
const po = observe('paint', (entry) => {
if (entry.name === 'first-paint') {
resolve(entry.startTime);
po.disconnect();
clearTimer();
}
})
})
- 首次内容绘制时间(FCP):
- 为首次有内容渲染的时间点
- 封装成一个promise 当入口名称为first-contentful-paint时将时间返回
// 首次内容绘制,标记浏览器渲染来自 DOM 第一位内容的时间点,该内容可能是文本、图像、SVG 甚至 元素
new Promise((resolve, reject) => {
const po = observe('paint', (entry) => {
if (entry.name === 'first-contentful-paint') {
resolve(entry.startTime);
po.disconnect();
clearTimer();
}
})
})
- dom加载解析完成:
// 当 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,无需等待样式表、图像和子框架的完成加载
// 借住performance 性能优化API取值
timing.domContentLoadedEventEnd - timing.fetchStart;
- 资源加载解析完成:
- loadEventEnd - fetchStart
// 当 HTML 文档被完全加载和解析完成之后,load 事件被触发 样式表、图像和子框架的完成加载
// 借住performance 性能优化API取值
timing.loadEventEnd - timing.fetchStart;
- 首字节响应时长:反应网络请求耗时
timing.responseStart - timing.fetchStart;
- 可交互时间:(TTI)
-
- 通过PerformanceObserver 监听资源的加载
-
- domContentLoadedEventEnd - navigationStart 粗略计算
-
- 使用谷歌提供更加精确的api tti-polyfill获取
-
import ttiPolyfill from 'tti-polyfill';
export default ttiPolyfill.getFirstConsistentlyInteractive();
-
最大内容渲染(LCP):
- 页面在加载过程中,是线性的,元素是一个一个渲染到屏幕上的,而不是一瞬间全渲染到屏幕上,所以“渲染面积”最大的元素随时在发生变化。
- 使用 PerformanceObserver 去捕获 LCP,会发现每当出现“渲染面积”更大的元素,就会捕获出一条新的性能条目。
- 监听largest-content-paint来进行获取数据
- 使用web-vitals 去计算LCP
import {getLCP} from 'web-vitals';
// Measure and log the current LCP value,
// any time it's ready to be reported.
getLCP(console.log);
- FMP 首次有意义渲染
- 通过new PerformanceObserver()去监控element, 给我们要监控标签的一个elementmeaning="meaning"属性, 我们就可以得到一个有意义内容渲染的时间
- FID 首次输入延迟
- 使用PerformanceObserver监控FID 设置监控类型为first-input
performance.now() 比 Date.now()
- 精确度更高
- 恒定的速率慢慢增加的,不受系统的影响
首屏加载时长
- 首屏时间 = 首屏内容渲染结束时间点 - 开始请求的时间点
performance.timing.unloadEventEnd - performance.timing.navigationStart
页面卡顿
- new PerformanceObserver
- entry.duration > 100 判断大于100ms,即可认定为长任务
- 使用 requestIdleCallback上报数据
- 响应用户交互的响应时间如果大于100ms,用户就会感觉卡顿
export function longTask() {
new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
if (entry.duration > 100) {
let lastEvent = getLastEvent();
requestIdleCallback(() => {
tracker.send({
kind: "experience",
type: "longTask",
eventType: lastEvent.type,
startTime: formatTime(entry.startTime), // 开始时间
duration: formatTime(entry.duration), // 持续时间
selector: lastEvent
? getSelector(lastEvent.path || lastEvent.target)
: "",
});
});
}
});
}).observe({ entryTypes: ["longtask"] });
}
接口慢请求
- 慢请求监控原理:
- 监控所有的资源加载(包括xhr和fetch请求)
- 过滤出请求时间大于自己的设定值的资源,
- 得到一系列的指标数据开始上报
- 实践
- window.PerformanceObserver的实例
- let listenObserverXhr = new window.PerformanceObserver(cb)
- listenObserverXhr.observe({ entryTypes: [‘resource’] });
- listenObserverXhr.disconnect() // 清空http监控
总结 性能监控(页面性能数据):
- 用户体验:
- TTFB:首字节时间(浏览器发出第一个请求到数据返回第一个字节所消耗的时间)可以来衡量服务器的处理能力
- FP:首次绘制(将第一个像素点绘制到屏幕的时刻)
- FCP:首次内容绘制(浏览器将第一个dom渲染到屏幕上的时间)
- FMP:首次有意义绘制(是页面可用性的度量标准)
- LCP:最大内容渲染(页面渲染内容最大)
- FID:首次输入延迟(用户首次和页面交互到页面响应交互的时间)
- dom解析完成
- 资源加载完成
- 如何计算数值:
-
- 可以根据performance.timing API手动计算一些值
-
- 可以根据performance.getEntriesByType获取页面绘制相关的一些数据比如:FP,FCP
-
- FMP首次有意义绘制,这个计算是根据业务需求来看的,比如我是一个搜索网站,那么我可以认为,页面中搜索框出来的时候就是有意义的时刻,我是不关心其他的一些广告啊还有外链什么的,这个时候我可以通过属性进行标识给输入框新增一个属性叫做elementtiming值为meaningful,然后通过一个新的api PerformanceObserver对改元素进行监控,我可以可以设置监控的类型为element,然后调用observe方法,当页面加载完毕浏览器会把监控到的数据通过条目列表返回给我们。
-
- 另外我们也可以将LCP看成是有意义的绘制
- 使用PerformanceObserver监控LCP计算LCP通过设置监控类型为,largest-content-paint来进行获取数据。
- 还有一个库 web-vitals可以用来获取LCP
- 使用PerformanceObserver监控FID 设置监控类型为first-input
- 根据LightHouse中的建议来优化我们的网站