关于前端性能监控,你需要知道这些(下)

上一节介绍了关于前端性能监控系统的性能监控两架马车性能指标部分,这节继续来介绍知道了指标之后,该如何测量上报呢?

一、数据采集

性能监控数据需要通过一定的采集方式进行统计,比如通过初始化的时候获取performance.timing去计算业务需要的指标;

在新标准中,通过PerformanceObserver来实时监听相关资源数据。下面我们通过一下常用的指标如FPFCPLCP演示如何收集相关数据。

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);

三、总结

以上介绍了关于前端性能监控链路闭环流程包含监控方式监控指标测量指标上报数据,而在实际的企业级应用中,这些数据还要经过反复测量、过滤等洗数据操作,才能为产品提供更加准确的监控数据、反映更加真实的系统运行状态。

除此之外,你还知道哪些,欢迎一起交流。

相关推荐
NoloveisGod2 分钟前
Vue的基础使用
前端·javascript·vue.js
GISer_Jing4 分钟前
前端系统设计面试题(二)Javascript\Vue
前端·javascript·vue.js
海上彼尚33 分钟前
实现3D热力图
前端·javascript·3d
杨过姑父33 分钟前
org.springframework.context.support.ApplicationListenerDetector 详细介绍
java·前端·spring
理想不理想v43 分钟前
使用JS实现文件流转换excel?
java·前端·javascript·css·vue.js·spring·面试
惜.己1 小时前
Jmeter中的配置原件(四)
java·前端·功能测试·jmeter·1024程序员节
EasyNTS1 小时前
无插件H5播放器EasyPlayer.js网页web无插件播放器vue和react详细介绍
前端·javascript·vue.js
poloma1 小时前
五千字长文搞清楚 Blob File ArrayBuffer TypedArray 到底是什么
前端·javascript·ecmascript 6
EasyNVR1 小时前
NVR录像机汇聚管理EasyNVR多品牌NVR管理工具视频汇聚技术在智慧安防监控中的应用与优势
安全·音视频·监控·视频监控
guokanglun1 小时前
Vue.js动态组件使用
前端·javascript·vue.js