零散专题36 前端性能监控
关于白屏时间和首屏时间以及FPS监控的学习笔记。
性能监控指标
本文主要从三个角度来进行性监控:
- 白屏时间:从浏览器响应用户输入网址地址,到开始显示内容的时间。
- 首屏时间:从浏览器响应用户输入网址地址,到首屏内容渲染完成的时间。
- 页面渲染帧率FPS:理论不应低于60FPS
Performance API
window.performance
对象提供了一组精确的数据,经过简单的计算就能够得出一些网页性能数据。
注意,现在查到的大多数文章使用的API都是performance.timing
这个接口,这个接口返回一个PerformanceTiming
对象,包含了页面相关的性能信息,但是这个接口已经被废弃,这样它上面定义的大量的属性,例如navigationStart
都已经被废弃,应该使用performance.timeOrigin
来代替。
为什么会被废弃呢?因为W3C提供了更全面、更强大的性能分析矩阵,其中最重要的工具之一就是High Resuolution Time这个基础的API,可以提供比Date.now()
更精准的时间戳。
所有的规范都是基于这个高精度的时间规范。Date
对象提取的时间单位都是毫秒,而使用performance.now()
(或者其他performance
获取到的时间单位)单位也是毫秒,但是返回变量精度达到了小数点后面10位以上,也就是说,performance.now()
精度可以达到微妙级别的精度,精度更高。
performance.now()
performance.now()
的返回值是一个高精度的时间戳,它与Date.now()
的主要区别是:
(1)performance.now()
精度使用了浮点数达到微秒级别的精确度,精度更高,用于和页面加载、渲染、性能监测等相关的场景
(2) performance.now()
兼容性差(IE10+)
(3)Date.now()
返回的是以1970-01-01T00:00:00Z
为起点的时间戳,依赖系统时间;而performance.now()
不受到系统时间的影响,返回当前网页自从performance.timeOrigin
到当前时间之间的毫秒数,它是规律增长的。
performance.timeOrigin
performance.timeOrigin
表示性能测试开始的时间点,即所有Performance Timeline起始点的时间,返回的是一个高精度的时间戳起点。
我理解为performance.timeOrigin
就是文档开始加载的起点,与之前的performance.timing.navigationStart
是相同的(实际上我在demo中测量出的时间,二者还是有一定的区别,不解)。
performance.mark()
performance.mark()
一般和performance.measure()
以及performance.getEntriesByName()
一起使用,可以更高精度的测量某个方法的用时
1 | performance.mark('fn_start'); |
白屏时间
白屏时间 = 地址栏输入网址后回车 - 浏览器出现第一个元素,这里面可能包括了DNS查询、TCP连接、网络请求、加载CSS等步骤。
影响白屏时间的因素:网络、服务端性能、前端页面结构设计。
计算白屏时间
「地址栏输入网址后回车」我们用performance.timeOrigin
来标识(或者使用以前的performance.timing.navigationStart
)
「浏览器出现第一个元素」也就是白屏时间的结束点,通常认为浏览器开始渲染<body>
或者解析完<head>
的时间就是白屏结束的时间点,具体实现的时候就是在<head>
的末尾添加一个<script>
脚本,在这个脚本中计算结束时间。
我们可以采取渐进式的方案,如果不支持performance
,则使用Date.now()
作为降级方案
1 |
|
首屏时间
对于页面加载时间,DOM提高的API都不能够用来直接计算首屏时间(onload
事件可以得到整个页面资源的加载完成,DOMConentLoad
事件可以得到页面DOM树构建完成的时间)
由于浏览器处理图片资源是异步的,并不会阻塞其他DOM节点的渲染,所以对于首屏有图片的情况,应当以耗时最长的图片加载完成的时间作为首屏时间的结束,如果没有图片则可以使用DOMConentLoad
打点时间
针对有图片的情况,至少要完成以下几个方面的工作:
- 判断屏幕尺寸
- 遍历所有图片,获得图片位置信息,筛选出处于首屏内图片
- 有图片的话,监听图片的
onload
事件,获取耗时最长的图片的加载时间 - 没有图片,监听
DOMConentLoad
事件获取事件
也可以通过MutationObserver
来监听页面变化,当首屏DOM节点稳定后,认为首屏加载完成,具体可以参考这两篇文章的实现(《精确并自动化地获取页面首屏时间》、《关于首屏时间采集自动化的解决方案》)。这样的方法有一定误差,并且实现起来也很繁琐。
但是在浏览器没有给出更多便捷的接口之前,可能也只能采取上面的某一种方式来实现了。
FPS监控
(1)开发环境
开发环境下FPS的监控可以使用开发者工具中,点击More tools
中的rendering
,勾选FPS meter
然后在屏幕的左上角就可以看到实时的FPS数据了:
(2)生产环境
可以通过requestAnimationFrame API
来执行一个函数,如果浏览器卡顿,那么函数执行的间隔变长,我们在函数中是可以感知到的。
1 | let lastTime = performance.now() |
通过上面的代码,我们就可以收集页面的近似FPS数据,结合自定义的卡顿标准(比如连续3个低于20FPS)就可以近似判断页面卡顿情况,进行上报
(3)Node环境
可以使用puppeteer
来访问页面,通过tracing
接口获得的trace.json
数据分析页面的FPS。
这个思路是在前公司,让我去分析页面的“跟手性”时实验过一点点的方向,当时是通过模拟CPU的卡顿,来比对页面被滑动时的时间差。
因为Puppeteer就是无头浏览器,浏览器中的接口一般都可以在其中找到,既然浏览器可以实时监控FPS数据,那么在Puppeteer中就可以获得对应的数据。
有机会再具体研究吧,不知道是否可行,但是是一个可以尝试的思路。