前端监控 SDK 的一些技术要点原理分析(上)

本文涉及的产品
应用实时监控服务ARMS - 应用监控,每月50GB免费额度
简介: 前端监控 SDK 的一些技术要点原理分析

一个完整的前端监控平台包括三个部分:数据采集与上报、数据整理和存储、数据展示。

本文要讲的就是其中的第一个环节——数据采集与上报。下图是本文要讲述内容的大纲,大家可以先大致了解一下:

仅看理论知识是比较难以理解的,为此我结合本文要讲的技术要点写了一个简单的监控 SDK,可以用它来写一些简单的 DEMO,帮助加深理解。再结合本文一起阅读,效果更好。

性能数据采集

chrome 开发团队提出了一系列用于检测网页性能的指标:

  • FP(first-paint),从页面加载开始到第一个像素绘制到屏幕上的时间
  • FCP(first-contentful-paint),从页面加载开始到页面内容的任何部分在屏幕上完成渲染的时间
  • LCP(largest-contentful-paint),从页面加载开始到最大文本块或图像元素在屏幕上完成渲染的时间
  • CLS(layout-shift),从页面加载开始和其生命周期状态变为隐藏期间发生的所有意外布局偏移的累积分数

这四个性能指标都需要通过 PerformanceObserver 来获取(也可以通过 performance.getEntriesByName() 获取,但它不是在事件触发时通知的)。PerformanceObserver 是一个性能监测对象,用于监测性能度量事件。

FP

FP(first-paint),从页面加载开始到第一个像素绘制到屏幕上的时间。其实把 FP 理解成白屏时间也是没问题的。

测量代码如下:

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

通过以上代码可以得到 FP 的内容:

{
    duration: 0,
    entryType: "paint",
    name: "first-paint",
    startTime: 359, // fp 时间
}

其中 startTime 就是我们要的绘制时间。

FCP

FCP(first-contentful-paint),从页面加载开始到页面内容的任何部分在屏幕上完成渲染的时间。对于该指标,"内容"指的是文本、图像(包括背景图像)、<svg>元素或非白色的<canvas>元素。

为了提供良好的用户体验,FCP 的分数应该控制在 1.8 秒以内。

测量代码:

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

通过以上代码可以得到 FCP 的内容:

{
    duration: 0,
    entryType: "paint",
    name: "first-contentful-paint",
    startTime: 459, // fcp 时间
}

其中 startTime 就是我们要的绘制时间。

LCP

LCP(largest-contentful-paint),从页面加载开始到最大文本块或图像元素在屏幕上完成渲染的时间。LCP 指标会根据页面首次开始加载的时间点来报告可视区域内可见的最大图像或文本块完成渲染的相对时间。

一个良好的 LCP 分数应该控制在 2.5 秒以内。

测量代码:

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

通过以上代码可以得到 LCP 的内容:

{
    duration: 0,
    element: p,
    entryType: "largest-contentful-paint",
    id: "",
    loadTime: 0,
    name: "",
    renderTime: 1021.299,
    size: 37932,
    startTime: 1021.299,
    url: "",
}

其中 startTime 就是我们要的绘制时间。element 是指 LCP 绘制的 DOM 元素。

FCP 和 LCP 的区别是:FCP 只要任意内容绘制完成就触发,LCP 是最大内容渲染完成时触发。

LCP 考察的元素类型为:

  • <img>元素
  • 内嵌在<svg>元素内的<image>元素
  • <video>元素(使用封面图像)
  • 通过url())函数(而非使用CSS 渐变)加载的带有背景图像的元素
  • 包含文本节点或其他行内级文本元素子元素的块级元素

CLS

CLS(layout-shift),从页面加载开始和其生命周期状态变为隐藏期间发生的所有意外布局偏移的累积分数。

布局偏移分数的计算方式如下:

布局偏移分数 = 影响分数 * 距离分数

影响分数测量不稳定元素对两帧之间的可视区域产生的影响。

距离分数指的是任何不稳定元素在一帧中位移的最大距离(水平或垂直)除以可视区域的最大尺寸维度(宽度或高度,以较大者为准)。

CLS 就是把所有布局偏移分数加起来的总和

当一个 DOM 在两个渲染帧之间产生了位移,就会触发 CLS(如图所示)。

上图中的矩形从左上角移动到了右边,这就算是一次布局偏移。同时,在 CLS 中,有一个叫会话窗口的术语:一个或多个快速连续发生的单次布局偏移,每次偏移相隔的时间少于 1 秒,且整个窗口的最大持续时长为 5 秒。

例如上图中的第二个会话窗口,它里面有四次布局偏移,每一次偏移之间的间隔必须少于 1 秒,并且第一个偏移和最后一个偏移之间的时间不能超过 5 秒,这样才能算是一次会话窗口。如果不符合这个条件,就算是一个新的会话窗口。可能有人会问,为什么要这样规定?其实这是 chrome 团队根据大量的实验和研究得出的分析结果 Evolving the CLS metric

CLS 一共有三种计算方式:

  1. 累加
  2. 取所有会话窗口的平均数
  3. 取所有会话窗口中的最大值

累加

也就是把从页面加载开始的所有布局偏移分数加在一起。但是这种计算方式对生命周期长的页面不友好,页面存留时间越长,CLS 分数越高。

取所有会话窗口的平均数

这种计算方式不是按单个布局偏移为单位,而是以会话窗口为单位。将所有会话窗口的值相加再取平均值。但是这种计算方式也有缺点。

从上图可以看出来,第一个会话窗口产生了比较大的 CLS 分数,第二个会话窗口产生了比较小的 CLS 分数。如果取它们的平均值来当做 CLS 分数,则根本看不出来页面的运行状况。原来页面是早期偏移多,后期偏移少,现在的平均值无法反映出这种情况。

取所有会话窗口中的最大值

这种方式是目前最优的计算方式,每次只取所有会话窗口的最大值,用来反映页面布局偏移的最差情况。详情请看 Evolving the CLS metric

下面是第三种计算方式的测量代码:

let sessionValue = 0
let sessionEntries = []
const cls = {
    subType: 'layout-shift',
    name: 'layout-shift',
    type: 'performance',
    pageURL: getPageURL(),
    value: 0,
}
const entryHandler = (list) => {
    for (const entry of list.getEntries()) {
        // Only count layout shifts without recent user input.
        if (!entry.hadRecentInput) {
            const firstSessionEntry = sessionEntries[0]
            const lastSessionEntry = sessionEntries[sessionEntries.length - 1]
            // If the entry occurred less than 1 second after the previous entry and
            // less than 5 seconds after the first entry in the session, include the
            // entry in the current session. Otherwise, start a new session.
            if (
                sessionValue
                && entry.startTime - lastSessionEntry.startTime < 1000
                && entry.startTime - firstSessionEntry.startTime < 5000
            ) {
                sessionValue += entry.value
                sessionEntries.push(formatCLSEntry(entry))
            } else {
                sessionValue = entry.value
                sessionEntries = [formatCLSEntry(entry)]
            }
            // If the current session value is larger than the current CLS value,
            // update CLS and the entries contributing to it.
            if (sessionValue > cls.value) {
                cls.value = sessionValue
                cls.entries = sessionEntries
                cls.startTime = performance.now()
                lazyReportCache(deepCopy(cls))
            }
        }
    }
}
const observer = new PerformanceObserver(entryHandler)
observer.observe({ type: 'layout-shift', buffered: true })

在看完上面的文字描述后,再看代码就好理解了。一次布局偏移的测量内容如下:

{
  duration: 0,
  entryType: "layout-shift",
  hadRecentInput: false,
  lastInputTime: 0,
  name: "",
  sources: (2) [LayoutShiftAttribution, LayoutShiftAttribution],
  startTime: 1176.199999999255,
  value: 0.000005752046026677329,
}

代码中的 value 字段就是布局偏移分数。

相关实践学习
通过云拨测对指定服务器进行Ping/DNS监测
本实验将通过云拨测对指定服务器进行Ping/DNS监测,评估网站服务质量和用户体验。
目录
相关文章
|
3天前
|
JSON 缓存 前端开发
个人练习前端技术使用Bootstrap、JQuery、thymeleaf
个人练习前端技术使用Bootstrap、JQuery、thymeleaf
10 2
|
5天前
|
移动开发 前端开发 JavaScript
浅谈前端路由原理hash和history
该文章详细解析了前端路由的两种模式——Hash模式与History模式的工作原理及其实现方式,并通过实例代码展示了如何在实际项目中运用这两种路由模式。
|
13天前
|
缓存 人工智能 前端开发
前端技术博客:探索现代前端开发的奥秘
前端技术博客:探索现代前端开发的奥秘
32 11
|
13天前
|
移动开发 缓存 前端开发
构建高效的前端路由系统:从原理到实践
在现代Web开发中,前端路由系统已成为构建单页面应用(SPA)不可或缺的核心技术之一。不同于传统服务器渲染的多页面应用,SPA通过前端路由技术实现了页面的局部刷新与无缝导航,极大地提升了用户体验。本文将深入剖析前端路由的工作原理,包括Hash模式与History模式的实现差异,并通过实战演示如何在Vue.js框架中构建一个高效、可维护的前端路由系统。我们还将探讨如何优化路由加载性能,确保应用在不同网络环境下的流畅运行。本文不仅适合前端开发者深入了解前端路由的奥秘,也为后端转前端或初学者提供了从零到一的实战指南。
|
2月前
|
前端开发 JavaScript 开发者
震惊!Web 前端 href 与 src 竟有如此差异,快来一探究竟,掌握热门技术核心要点
【8月更文挑战第26天】在Web前端开发中,`href`与`src`是两个常用属性,但其差异常被忽视。`href`(超文本引用)用于创建文档间的链接关系,如链接至外部网页或引入CSS文件;`src`(来源)则用于在文档内嵌入资源,如图片或JavaScript文件。两者在使用场景及加载机制上有所不同:`href`支持并行下载且不阻塞渲染,适合非关键资源加载;而`src`加载时会暂停页面渲染直至资源加载完成,适用于如图片和脚本这类对页面显示至关重要的资源。因此,正确理解并运用这两个属性对于保障网页性能和用户体验至关重要。
41 3
|
2月前
|
存储 移动开发 前端开发
HTML5时代来临,这些新特性你掌握了吗?一篇文章带你玩转Web前端技术潮流!
【8月更文挑战第26天】HTML5(简称H5)作为新一代Web标准,相比HTML4带来了诸多增强功能。
43 2
|
2月前
|
前端开发 Java Spring
Spring与Angular/React/Vue:当后端大佬遇上前端三杰,会擦出怎样的火花?一场技术的盛宴,你准备好了吗?
【8月更文挑战第31天】Spring框架与Angular、React、Vue等前端框架的集成是现代Web应用开发的核心。通过RESTful API、WebSocket及GraphQL等方式,Spring能与前端框架高效互动,提供快速且功能丰富的应用。RESTful API简单有效,适用于基本数据交互;WebSocket支持实时通信,适合聊天应用和数据监控;GraphQL则提供更精确的数据查询能力。开发者可根据需求选择合适的集成方式,提升用户体验和应用功能。
67 0
|
2月前
|
开发者 安全 UED
JSF事件监听器:解锁动态界面的秘密武器,你真的知道如何驾驭它吗?
【8月更文挑战第31天】在构建动态用户界面时,事件监听器是实现组件间通信和响应用户操作的关键机制。JavaServer Faces (JSF) 提供了完整的事件模型,通过自定义事件监听器扩展组件行为。本文详细介绍如何在 JSF 应用中创建和使用事件监听器,提升应用的交互性和响应能力。
22 0
|
2月前
|
前端开发 大数据 数据库
🔥大数据洪流下的决战:JSF 表格组件如何做到毫秒级响应?揭秘背后的性能魔法!💪
【8月更文挑战第31天】在 Web 应用中,表格组件常用于展示和操作数据,但在大数据量下性能会成瓶颈。本文介绍在 JavaServer Faces(JSF)中优化表格组件的方法,包括数据处理、分页及懒加载等技术。通过后端分页或懒加载按需加载数据,减少不必要的数据加载和优化数据库查询,并利用缓存机制减少数据库访问次数,从而提高表格组件的响应速度和整体性能。掌握这些最佳实践对开发高性能 JSF 应用至关重要。
45 0
|
2月前
|
大数据 数据处理 分布式计算
JSF 逆袭大数据江湖!看前端框架如何挑战数据处理极限?揭秘这场技术与勇气的较量!
【8月更文挑战第31天】在信息爆炸时代,大数据已成为企业和政府决策的关键。JavaServer Faces(JSF)作为标准的 Java Web 框架,如何与大数据技术结合,高效处理大规模数据集?本文探讨大数据的挑战与机遇,介绍 JSF 与 Hadoop、Apache Spark 等技术的融合,展示其实现高效数据存储和处理的潜力,并提供示例代码,助您构建强大的大数据系统。
31 0
下一篇
无影云桌面