上一节介绍了关于前端性能监控系统的
性能监控两架马车
和性能指标
部分,这节继续来介绍知道了指标之后,该如何测量 和上报呢?
一、数据采集
性能监控数据需要通过一定的采集方式进行统计,比如通过初始化的时候获取performance.timing
去计算业务需要的指标;
在新标准中,通过PerformanceObserver
来实时监听相关资源数据。下面我们通过一下常用的指标如FP
、FCP
、LCP
演示如何收集相关数据。
FP
FP(first-paint)
,从页面加载开始到第一个像素绘制到屏幕上的时间。一定程度上 FP 理解成白屏时间也是没问题的。
测量示例:
js
const entryHandler = (list) => {
for (const entry of list.getEntries()) {
if (entry.name === 'first-paint') {
observer.disconnect()
}
console.log(entry)
}
}
const observer = new PerformanceObserver(entryHandler)
// buffered 属性表示是否观察缓存数据,也就是说观察代码添加时机比事情触发时机晚也没关系。
observer.observe({ type: 'paint', buffered: true })
测量的结果如下:
js
{
"name": "first-paint",
"entryType": "paint",
"startTime": 2664.7000000001863,
"duration": 0
}
即startTime
未我们想要的FP
结果。
FCP
FCP(first-contentful-paint)
,从页面加载开始到页面内容的任何部分在屏幕上完成渲染的时间。对于该指标,"内容"指的是文本、图像(包括背景图像)、<svg>
元素或非白色的<canvas>
元素。
注意:良好的性能数据统计
FCP
分数应该控制在1.8s
以内。
js
// 测量示例
const entryHandler = (list) => {
for (const entry of list.getEntries()) {
if (entry.name === 'first-contentful-paint') {
observer.disconnect()
}
console.log(entry)
}
}
const observer = new PerformanceObserver(entryHandler)
observer.observe({ type: 'paint', buffered: true })
// 测量结果
{
duration: 0,
entryType: "paint",
name: "first-contentful-paint",
startTime: 459, // fcp 时间
}
LCP
LCP(largest-contentful-paint)
,从页面加载开始到最大文本块或图像元素在屏幕上完成渲染的时间。LCP 指标会根据页面首次开始加载的时间点来报告可视区域内可见的最大图像或文本块完成渲染的相对时间。
注意:一个良好的 LCP 分数应该控制在 2.5 秒以内。
js
// 测量示例
const entryHandler = (list) => {
if (observer) {
observer.disconnect()
}
for (const entry of list.getEntries()) {
console.log(entry)
}
}
const observer = new PerformanceObserver(entryHandler)
observer.observe({ type: 'largest-contentful-paint', buffered: true })
//observer.observe({ type: 'element', buffered: true })
// 测量结果
{
"name": "",
"entryType": "largest-contentful-paint",
"startTime": 402.599,
"duration": 0,
"size": 1627,
"renderTime": 402.599,
"loadTime": 0,
"firstAnimatedFrameTime": 0,
"id": "",
"url": ""
}
其中 startTime
就是我们要的绘制时间。element
是指 LCP 绘制的 DOM 元素。
FCP 和 LCP 的区别是:FCP 只要任意内容绘制完成就触发,LCP 是最大内容渲染完成时触发。
有一个注意点,如果你去测量过一个网站的LCP,它并不一定就是一整块相对完整的内容,有可能是一个图片,有可能是一块文本块内容,而不是如上图所示LCP是一块相对完整的快照。
LCP 考察的元素类型为:
<img>
元素- 内嵌在
<svg>
元素内的<image>
元素 <video>
元素(使用封面图像)- 通过[
url()
]函数(而非使用CSS 渐变)加载的带有背景图像的元素 - 包含文本节点或其他行内级文本元素子元素的块级元素。
问题来了,那如果我们期望用户想第一时间看到的文本或者图片并不是LCP统计的内容怎么办?即LCP统计出来的内容并不是主要内容。
我们可以使用 Element Timing API 来设置自己的自定义指标。
html
<img... elementtiming='foobar'/>
<p elementtiming='important-paragraph'>This is text I care about.</p>
Vue应用页面切换渲染耗时
思路:通过自定义插件的形式,给vue
应用初始化页面耗时统计逻辑,其中通过beforeEach
钩子记录进入时间startTime
,通过mixin
在每个路由中mounted
钩子中统计endTime
,在一定的时间之后进行上报数据。
代码如下:
js
// 业务路由逻辑
import router from './router'
export default {
install(Vue, options) {
let startTime = 0
let isFirst = true
let timer = null
router.beforeEach((to, from, next) => {
// 首次进入页面已经有其他统计的渲染时间可用
if (isFirst) {
isFirst = false
return next()
}
// 给 router 新增一个字段,表示是否要计算渲染时间
// 只有路由跳转才需要计算,组件更新的时候触发mounted不需要监听
router.needCalculateRenderTime = true
startTime = performance.now()
next()
})
Vue.mixin({
mounted() {
if (!router.needCalculateRenderTime) return
this.$nextTick(() => {
// 仅在整个视图都被渲染之后才会运行的代码
const now = performance.now()
clearTimeout(timer)
timer = setTimeout(() => {
router.needCalculateRenderTime = false
ykReport({
type: 'performance',
subType: 'vue-router-change-paint',
duration: now - startTime,
startTime: now,
pageURL: getPageURL(),
})
}, 1000)
})
},
})
}
}
以上演示了部分常见性能监控指标,除此之外你还知道有哪些指标可以作为性能监控指标呢?
二、上报数据
sendBeacon
一般可以考虑在用户准备卸载页面时上报,毫无疑问这个时间点不会干扰用户在当前页的操作。 但是如果上报耗时很长,会影响用户跳转到下一页的体验。可以使用 navigator.sendBeacon
js
window.addEventListener('unload', function() {
// 注意 performance.getEntries 会取当前页所有资源包括页面本身的性能信息
let rumData = new FormData();
rumData.append('entries', JSON.stringify(performance.getEntries()));
// 是否支持
if('sendBeacon' in navigator) {
// Beacon 发起请求
if(navigator.sendBeacon(endpoint, rumData)) {
// sendBeacon 发送成功
} else {
// sendBeacon 发送失败! 使用 XHR or fetch 代替
}
} else {
// sendBeacon 不支持! 使用 XHR or fetch 代替
}
}, false);
三、总结
以上介绍了关于前端性能监控链路闭环流程包含监控方式
、监控指标
、测量指标
和上报数据
,而在实际的企业级应用中,这些数据还要经过反复测量、过滤等洗数据
操作,才能为产品提供更加准确的监控数据、反映更加真实的系统运行状态。
除此之外,你还知道哪些,欢迎一起交流。