前端性能监控相关指标

最初,评价前端页面加载性能有两个指标:DOMContentLoadedload事件,分别代表DOM构造完成和首屏资源加载完成。

DOM 文档加载步骤:

  1. 解析 html 结构
  2. 加载外部脚本和样式表文件
  3. 解析并执行脚本代码
  4. 构造 HTML DOM 模型 (DOMContentLoaded 执行点)
  5. 加载图片等外部文件
  6. 页面加载完毕 (触发load

loadDOMContentLoaded的不同,load是在第六步完成之后执行,而DOMContentLoaded是在第四步完成之后执行。很明显DOMContentLoaded的执行是在load之前的。

当纯 HTML 被完全加载以及解析时,DOMContentLoaded 事件会被触发,不用等待 cssimgiframe 加载完。

当整个页面及所有依赖资源如样式表和图片都已完成加载时,将触发 load 事件。

对于之前的页面和现代的服务端渲染的页面,这两个指标都可以很好地衡量首屏内容展示时间。但对于现代复杂的单页应用,都是通过JS操作DOM向页面添加主要内容,对于这种场景,DOMContentLoadedload事件就不能很好地衡量首屏显示时间了。

于是有FPFCPFMP被提出来,它们关注的不是“加载”,而是“渲染”,因此能更好地表现用户看到的情况。我们来看看具体看看这些指标。

FP

FP(first-paint),从页面加载开始到第一个像素绘制到屏幕上的时间。

有节点不一定有渲染,如果没有任何样式,是没有界面的,也不需要渲染。

比如

<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title>no FP</title></head><body><div></div></body>
</html>

同时,渲染的操作一定是发生在视口内的,对于视口外不可见的内容,不会触发“Paint”操作,比如下面代码,不会触发FP事件。

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>no FP</title><style>div {position: absolute;left: -99999px;width: 1px;height: 1px;background-color: red;float: left;}</style>
</head>
<body><div></div>
</body>
</html>

指标的计算可以通过performance这个api

const fp = performance.getEntries('paint').filter(entry => entry.name == 'first-paint')[0].startTime;

FCP

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

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

指标的计算:

const fcp = performance.getEntries('paint').filter(entry => entry.name == 'first-contentful-paint')[0].startTime;

FP与FCP比较

FP 发生的时间一定小于等于 FCP

FP 指的是绘制像素,比如说页面的背景色是灰色的,那么在显示灰色背景时就记录下了 FP 指标。但是此时 DOM 内容还没开始绘制,可能需要文件下载、解析等过程,只有当 DOM 内容发生变化才会触发,比如说渲染出了一段文字,此时就会记录下 FCP 指标。因此说我们可以把这两个指标认为是和白屏时间相关的指标,所以肯定是最快越好。

LCP

LCP(largest-contentful-paint),从页面加载开始到最大文本块或图像元素在屏幕上完成渲染的时间(该时间会随着页面渲染变化而变化,因为页面中的最大元素在渲染过程中可能会发生改变,另外该指标会在用户第一次交互后停止记录)。LCP 指标会根据页面首次开始加载的时间点来报告可视区域内可见的最大图像或文本块完成渲染的相对时间。

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

LCP 其实能比前两个指标更能体现一个页面的性能好坏程度,因为这个指标会持续更新。举个例子:当页面出现骨架屏或者 Loading 动画时 FCP 其实已经被记录下来了,但是此时用户希望看到的内容其实并未呈现,我们更想知道的是页面主要的内容是何时呈现出来的。

LCP 考察的元素类型为:

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

指标的计算:

new PerformanceObserver((entryList) => {for (const entry of entryList.getEntries()) {console.log('LCP candidate:', entry.startTime, entry);}
}).observe({type: 'largest-contentful-paint', buffered: true});

TTI

TTITime to Interactive),首次可交互时间,即DOM加载并解析完成后,界面上的元素可以交互(如输入框可以输入、按钮可以点击、超长元素可以滚动)。

很多情况下,开发者往往只关注页面渲染相关的指标,如 FPFCP 等,而忽视了页面的可用性指标。TTI 即是反映页面可用性的重要指标。TTI 值越小,代表用户可以更早地操作页面,用户体验就更好。

什么是完全可交互状态的页面:

  1. 页面已经显示有用内容。
  2. 页面上的可见元素关联的事件响应函数已经完成注册。
  3. 事件响应函数可以在事件发生后的 50ms 内开始执行(主线程无 Long Task)。

这个指标计算过程略微复杂,它需要满足以下几个条件

  1. FCP 指标后开始计算
  2. 持续 5 秒内无长任务(执行时间超过 50 ms)且无两个以上正在进行中的 GET 请求

长任务为什么需要定义为 50ms 以外?
Google 提出了一个 RAIL 模型:对于用户交互(比如点击事件),推荐的响应时间是 100ms 以内。那么为了达成这个目标,推荐在空闲时间里执行任务不超过 50msW3C 也有这样的标准规定),这样能在用户无感知的情况下响应用户的交互,否则就会造成延迟感。

  1. 往前回溯至 5 秒前的最后一个长任务结束的时间。

指标的计算:

const tti = performance.timing.domInteractive - performance.timing.fetchStart

长任务
阻塞主线程达 50 毫秒或以上的任务,可以通过PerformanceObserver获取

const observer = new PerformanceObserver((list) => {for (const entry of list.getEntries()) {console.log('longtask candidate: ', entry.startTime);}
});
observer.observe({entryTypes: ['longtask']});

TBT

TBTTotal Blocking Time),阻塞总时间,记录在 FCPTTI 之间所有长任务的阻塞时间总和。

假如说在 FCPTTI 之间页面总共执行了以下长任务(执行时间大于 50ms)及短任务(执行时间低于 50ms)

那么每个长任务的阻塞时间就等于它所执行的总时间减去 50ms

所以对于上图的情况来说,TBT 总共等于 345ms。

这个指标的高低其实也影响了 TTI 的高低,或者说和长任务相关的几个指标都有关联性。

FID

FIDFirst Input Delay) 用于度量用户第一次与页面交互的延迟时间,是用户第一次与页面交互到浏览器真正能够开始处理事件处理程序以响应该交互的时间。

相对于TTIFID表示实际的用户操作的延时,更能从用户角度反映网页的交互性能。

指标的计算:

const observer = new PerformanceObserver((list) => {for (const entry of list.getEntries()) {console.log('fid', entry.processingStart - entry.startTime);}
});observer.observe({type: 'first-input', buffer: true});

CLS

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

看起来有点复杂,这里做一个简单的解释:

  • 不稳定元素:一个非用户操作但发生较大偏移的可见元素称为不稳定元素。
  • 布局变化得分:元素从原始位置偏移到当前位置影响的页面比例 * 元素偏移距离比例。

当一个 DOM 在两个渲染帧之间产生了位移,就会触发 CLS

大家想必遇到过这类情况:页面渲染过程中突然插入一张巨大的图片或者说点击了某个按钮突然动态插入了一块内容等等相当影响用户体验的网站。这个指标就是为这种情况而生的,计算方式为:位移影响的面积 * 位移距离。

以上图为例,文本移动了 25% 的屏幕高度距离(位移距离),位移前后影响了 75% 的屏幕高度面积(位移影响的面积),那么 CLS0.25 * 0.75 = 0.1875

网站应努力使 CLS 分数小于 0.1 。

指标计算

let clsValue = 0;
let clsEntries = [];
let sessionValue = 0;
let sessionEntries = [];
new PerformanceObserver((entryList) => {for (const entry of entryList.getEntries()) {// 用户没有在输入时才开始计算if (!entry.hadRecentInput) {const firstSessionEntry = sessionEntries[0];const lastSessionEntry = sessionEntries[sessionEntries.length - 1];// 当新的entry比前一个entry的渲染时间小于1秒以及比最早的entry小于5秒,才进行收集,否则重新收集if (sessionValue&& entry.startTime - lastSessionEntry.startTime < 1000&& entry.startTime - firstSessionEntry.startTime < 5000) {sessionValue += entry.value;sessionEntries.push(entry);} else {sessionValue = entry.value;sessionEntries = [entry];}if (sessionValue > clsValue) {clsValue = sessionValue;clsEntries = sessionEntries;console.log('CLS:', clsValue, clsEntries)}}}
}).observe({type: 'layout-shift', buffered: true});

FPS

Chrome DevTool 中有一栏 Rendering 中包含 FPS 指标,但目前浏览器标准中暂时没有提供相应 API ,只能手动实现。这里需要借助 requestAnimationFrame 方法模拟实现,浏览器会在下一次重绘之前执行 rAF 的回调,因此可以通过计算每秒内 rAF 的执行次数来计算当前页面的 FPS

FPS过低会让用户感觉卡顿,因此这个计算可以用来监控页面卡顿情况。

最优的帧率是 60,即16.5ms 左右渲染一次

var lastTime = performance.now();
var frame = 0;
var lastFameTime = performance.now();
var loop = function(time) {var now =  performance.now();var fs = (now - lastFameTime);lastFameTime = now;var fps = Math.round(1000/fs);frame++;if (now > 1000 + lastTime) {var fps = Math.round( ( frame * 1000 ) / ( now - lastTime ) );frame = 0;    lastTime = now;    };           window.requestAnimationFrame(loop);
}

通俗地解释就是,通过 requestAnimationFrame API 来定时执行一些 JS 代码,如果浏览器卡顿,无法很好地保证渲染的频率,1s 中 frame 无法达到 60 帧,即可间接地反映浏览器的渲染帧率。

连续出现3个低于20的 FPS 即可认为网页存在卡顿

function isBlocking(fpsList, below=20, last=3) {var count = 0for(var i = 0; i < fpsList.length; i++) {if (fpsList[i] && fpsList[i] < below) {count++;} else {count = 0}if (count >= last) {return true}}return false
}

性能优化

FPFCP

  • 消除阻塞资源
  • 缩小 CSS
  • 移除未使用的CSS
  • 预连接到所需的来源
  • 减少服务端响应时间(TTFB)
  • 避免多个页面重定向
  • 预加载关键请求
  • 避免巨大的网络负载
  • 使用高效缓存策略去缓存静态资源
  • 避免 DOM 过大
  • 最小化关键请求深度
  • 确保文本在网页字体加载期间保持可见
  • 保持较少的请求数以及较小的传输大小

LCP

LCP 主要受四个因素影响:

  • 缓慢的服务器响应速度
  • JavaScript 和 CSS 渲染阻塞
  • 资源加载时间
  • 客户端渲染

可以:

  • 优化JavaScript(针对客户端渲染的网站)
  • 优化css
  • 优化图片

TTI

  • 缩小 JavaScript 体积
  • 预连接到所需的来源
  • 加载关键请求
  • 减少第三方代码的影响
  • 最小化关键请求深度
  • 减少 JavaScript 执行时间
  • 最小化主线程工作
  • 保持较低的请求数和较小的传输大小

CLS

  • 在图片和视频元素中包含大小属性,或者用 CSS 长宽比框之类的东西保留所需的空间。
  • 不要在现有内容之上插入内容,除非是为了响应用户交互。
  • 多用 transform animations,而不是触发布局变化的 animations properties

FID

  • 减少第三方代码的影响
  • 减少 JavaScript 执行时间
  • 最小化主线程工作
  • 保持较少的请求数以及较小的传输大小

前端性能监控相关指标相关推荐

  1. 支付宝蚂蚁金服是怎么把前端性能监控做到极致的?

    本文来自蚂蚁金服前端技术专家杨森在 ArchSummit 北京 2018 的分享,他将分享如何通过 Performance 相关的 API 准确的采集用户性能数据,并如何通过大数据计算加工最终产出用户 ...

  2. 百万 QPS 前端性能监控系统设计与实现

    作者:李振,腾讯云前端性能监控负责人 什么是前端性能监控(RUM) 腾讯云前端性能监控 (RUM) 是一站式前端监控解决方案,用户只需要安装 SDK 到自己的项目中,通过简单配置化,即可实现对用户页面 ...

  3. 前端性能监控技术方案

    前端性能监控主要考虑以下几个方面 1. 静态性能 静态性能包括:包体积分析.lightHouse 2. 动态性能 (1)首屏:FMP.TTI.FCP.FP.满开比.秒开率.页面渲染数据时长 (2)卡顿 ...

  4. 7 天打造前端性能监控系统

    2019独角兽企业重金招聘Python工程师标准>>> Day1.为什么要监控性能? "If you cannot measure it, you cannot impro ...

  5. Performance — 前端性能监控利器

    2019独角兽企业重金招聘Python工程师标准>>> 最近在写一个监控脚本,终于有机会接触到了这一块,整理后写下了本文. Performance是一个做前端性能监控离不开的API, ...

  6. zanePerfor 一款完整,高性能,高可用的前端性能监控系统,不要错过

    HI!,你好,我是zane,zanePerfor是一款最近我开发的一个前端性能监控平台,现在支持web浏览器端和微信小程序段. 我定义为一款完整,高性能,高可用的前端性能监控系统,这是未来会达到的目的 ...

  7. 前端白屏问题_深入理解前端性能监控

    在同样的网络环境下,有两个同样能满足你的需求的网站,一个唰的一下就加载出来了,另一个白屏转圈转了半天内容才出来,如果让你选择,你会用哪一个? 页面的性能问题是前端开发中一个重要环节,但一直以来我们没有 ...

  8. 谷歌测试工程师分享前端性能监控利器Performance

    最近在写一个监控脚本,终于有机会接触到了这一块,整理后写下了本文. Performance是一个做前端性能监控离不开的API,最好在页面完全加载完成之后再使用,因为很多值必须在页面完全加载之后才能得到 ...

  9. Performance --- 前端性能监控

    阅读目录 一:什么是Performance? 二:使用 performance.timing 来计算值 三:前端性能如何优化? 四:Performance中方法 五:使用performane编写小工具 ...

最新文章

  1. portlet java_探秘企业门户开发:Java Portlet入门(2)
  2. 网络营销中一旦网站改版需要遵循哪些网络营销原则呢?
  3. hdu 4160 Dolls 匈牙利算法求最大匹配
  4. boost::sort::sample_sort相关的测试程序
  5. 选择排序(C++版)
  6. stl swap函数_vector :: swap()函数以及C ++ STL中的示例
  7. python星空代码_用python画星空源代码是什么?
  8. python mssqlserver_python for MSSQLserver
  9. [转载] Python集合set
  10. 吴恩达深度学习笔记 course2 week2 优化算法
  11. 点评一下阿提亚和黎曼猜想
  12. 计算机上word如何批量打印,word批量调页面设置 关于Word如何批量打印
  13. creo扫描选择多条链作为轨迹_Creo与Proe可变截面扫描关系式雨伞建模实例-一加一学院...
  14. 基于协同过滤算法的旅游推荐系统
  15. 苹果x和xs买哪个好_苹果12哪个颜色销量销售好 iPhone12哪个颜色好看
  16. c# timer 销毁_.NET中Timer 如何正确地被Dispose
  17. 机房收费系统之思想性总结
  18. jedis异常:Could not get a resource from the pool
  19. 【Hexo】Hexo搭建Butterfly主题并快速美化
  20. 选择美国虚拟主机时要考虑的事项

热门文章

  1. AttributeError: builtin_function_or_method object has no attribute mktime
  2. div内元素不在一行的问题解决方法
  3. WordPress必装插件推荐
  4. 科研—画图图片处理1
  5. matlab命令行清,如果需要清除MATLAB命令行窗口的以往输出结果,可以通过在命令行窗口中输入clear命令实现。 答案:错...
  6. library not found for -lstdc++和dyld: Library not loaded: /usr/lib/swift/libswiftCoreGraphics.dylib解决
  7. 基于微信小程序+springboot的在线商城系统毕业设计源码
  8. hive 压缩格式汇总
  9. 如何管理计算机中文件,如何管理文件 -电脑资料
  10. 阿里云产品 系列(一)MaxCompute简介与使用--上