你以为什么是perfomance之前端基础性能监测

4,484 阅读8分钟

简说

前端基础性能监测是用于制定提升用户体验方案所依赖的重要流程。是现代前端业务中无法忽视的重要一环。

合成监控 (synthetic monitoring - SYN)

通过工具运行页面,进而采集性能指标,形成报告。

例:谷歌工具-lighthouse=》

  1. 下载谷歌插件,然后使用时,直接点击插件或者在开发者工具中点击Audits进行性能报告生成。
  2. 使用命令行,安装node(版本5以上),然后npm run install lighthouse,使用lighthouse http/https://xxx进行性能报告生成。(帮助:lighthouse —help)。

特点

难度成本低,采集到的数据指标丰富,但是样本少。

真实用户监控(real user monitoring - rum)

用户访问页面后将产生各类性能指标,选择合适的时间/事件点上传指标,提取并加工数据,最后形成可视化结果。

特点

难度成本高,采集到的数据指标相对不那么丰富,但是样本量大。

需要采用标准的api

High Resolution Time Level 2

表示高精确度时间规范,支持应用程序内客户端延迟测量。该规范中时间的精确度达到微妙(千分之一毫秒)级别,同时支持单调时钟,即始终增加不受干扰的时钟。

Perfomance Timeline level 2:

Perfomance Timeline本身是用来支持客户端延迟测量的规范,而Perfomance Timeline level2将在Perfomance Timeline的基础上,增加了timeOrigin、performance.now()、Performance.Observer()、entry相关(你以为什么是perfomance之基础属性中有介绍)。

Resource Time Level 2:

通过resource timing api可以获取应用资源(如:css、js、img、接口请求数据等)加载的详细计时数据,该api返回的数据都是高精度时间戳(即High Resolution Time)。注意:该api统计的数据存放在entryType为“resource”的entry中;获取方式如下:

Navigation Timing Level 2:

提供了网站性能数据,使用Naviagtion Timing API获取,如下:

Server Timing:
  • 概念

一个请求内,服务器在响应过程中,各步骤耗时指标。

  • 意义

当出现性能问题时,无需前端排查(如F12),直接定位服务端是否存在问题。

  • 本质

是返回头信息的一种,存在于respose header中,需要后端配合设置,Server Timing受同源策略限制,需要后端在respose header中设置Timing-Allow-Origin白名单,也可以设置为*(即通过所有)。

  • 详解

Server-Timing:db;dur=100 中,db;dur=100 是一个 server-timing-metric ,其中 db 是 metric-name ,分号 (;) 后的 dur=100 是 server-timing-param ,其中 dur 是 server-timing-param-name ,等于号 (=) 后面的 100 是 server-timing-param-value 。 Server-Timing:db;dur=100 指的是服务器查询数据库花费100ms(一般都会设定一个时间值为临界,超过则为性能问题)。

  • performance server timing

performance 的每一个 navigation/resouce 的 entry 中,都会有一个serverTiming 数组,用来存放Server-Timing 中的所有参数,数组中每个对象都是一个性能指标(即为一个 server-timing-metric 并对应一个 metric-name),每个对象都有三个参数:name(即metric-name)、duration(即毫秒时间)、description(即说明解释)。若后端不设置请求头中的Server-Timing或被跨域限制,那么 performance.getEntriesByType("resource")[ind(任一个)].serverTiming 为空数组。

  • 支持

截止19.09.09,谷歌65+、火狐61+支持。

paint timing:

提供一个页面在构建过程中绘制/渲染(即渲染树转换为页面元素)的各个时间点。

其中first-paint表示首次渲染,即第⼀个非网页背景像素渲染;而first-contentful-paint表示首次内容渲染,即第一个⽂本、图像、背景图片或非白色 canvas/SVG 渲染。

frame timing:
  • 概念

提供浏览器事件循环的帧计时数据。

  • 一帧

表示浏览器在一个事件循环迭代中所做的工作量。如处理dom、调整大小、滚动、渲染、css动画等。

  • 体验

刷新速率:60Hz,对应帧率:60fps(每秒帧),可以满足一个较良好的体验。所以需要浏览器16.7ms处理一帧。

  • frame entry对象

entrytype:’frame’、name:地址、starttime:开始时间、duration:两帧间隔时间。

  • 获取方式

performance.getEntrysByType(‘frams’)。

  • 监听方式
var observer = new PerformanceObserver(function(list, obj) {
                        console.log('list',list);
                        console.log('obj',obj);
                    });
                    observer.observe({entryTypes: ["frame"]});

其中,observer 是观察者对象,new PerformanceObserver(function(list, obj) {});是在生成观察者的同时注册回调函数, observer.observe({entryTypes: ["frame"]});是表示监听哪些类型的entry对象的变化。

PerformanceObverser:
var callback = function(list, obj) {
          console.log('list',list);
          console.log('obj',obj);
}
var observer = new PerformanceObserver(callback);
observer.observe({entryTypes: [“resource"]});

其中new PerformanceObserver(callback);实例化了构造函数PerformanceObserver,注册了回调函数callback,反回了观察者对象。 observer.observe({entryTypes: [“resource"]});中,表示监听了resource类型entry的变化。 回调中的list和obj如下:

user timing:

User Timing表示使用者自行创建时间戳,生成的entry对象类型为 mark 和 meature 。 详细介绍:(你以为什么是perfomance之基础属性中有介绍)。

Long Tasks:
  • 概念

过滤出并告诉我们哪些任务是阻塞主线程50ms以上的任务。

  • 阈值来源=》50毫秒这个阈值标准来源于《RAIL Model》中 "Response: process events in under 50ms" 章节。将带来以下问题:
  1. 可交互时间 延迟
  2. 严重不稳定的交互行为的延迟 (单击、滚动、滚轮等)
  3. 严重不稳定的事件回调延迟
  4. 紊乱的动画和滚动
  • 长任务案例

耗时过长的事件回调、代价过大的回流重绘、浏览器在超过50ms的事件循环中相邻循环间隔中所做工作。

  • entry获取

performance.getEntrysByType(‘longtask’)。可以使用ovserver建立监听任务。

performance timing:

详细介绍:(你以为什么是perfomance之基础属性中有介绍)。

level2是什么?

performance timeline,是w3c小组制定的规范,定义了用来在应用生命周期内各性能指标的接口。 performance timeline level2是其第二版标准,包含:

  • performance基本接口的扩展
  • 在web workers中暴露performanceEntry
  • 增加了performanceObserver的支持 node同样支持performacne API,在浏览器中,通过 const {performance={},PerformanceObserver={}} = window获取,在node中,通过 const {performance={},PerformanceObserver={}} = require(‘perf_hooks’)获取。 PerformanceObserver: 新的性能指标api需要通过PerformanceObserver获取,但是PerformanceObserver的浏览器兼容性不是特别好。 PerformanceObserver实现了对performance的监听:
  • 监听事件发生,避免过去轮询观察
  • 避免重复逻辑来获取不同性能指标
  • 避免其他资源操作浏览器缓冲区时产生竞态关系

根据需求定义采集指标(这里提供一些指标)

  • 页面加载时长(Page Load Time,PLT) =》load 事件触发完成,loadEventEnd - fetchStart。
  • 首次渲染时长/白屏时间(First Paint)=》首次渲染时间(第⼀个非网页背景像素渲染),firstPaintTime - fetchStart。
  • 首次内容渲染时长(First Contentful Paint)=》首次内容渲染时间(第一个 ⽂本、图像、背景图片或非白色 canvas/SVG 内容渲染),firstContentPaintTime - fetchStart。
  • 开始渲染(Start Render)=》TTFB(发起请求到服务器返回数据的时间) + TTDD(从服务器加载HTML文档的时间) + TTHE(HTML文档头部解析完成所需要的时间),domContentLoadedEventStart - domainLookupStart。
  • 首字节等待时长(Time To First Byte,TTFB)
  • DOM Ready 时长/首次可交互时长(First Interactive)
  • 首次CPU空闲时长(First CPU Idle)
  • Speed Index(在加载过程中视觉上的变化速度,采用了 Mean Pixel-Histogram Difference 算法,blog.csdn.net/bulprezht1i…)
  • Perceptual Speed Index(在加载过程中视觉上的变化速度,Structural Similarity Image Metric 算法,blog.csdn.net/bulprezht1i…)
  • Last Paint Heros
  • Paint Phase Timing
  • 视觉完整时间(Visually Complete)
  • 首次有效渲染时长(First Meaningful Paint)
  • 首屏加载时长(Above-The-Fold Time,AFT)
  • DOM Complete 时长
  • DOM Interative 时长

单页面中的性能监控可能存在的问题

  1. 虽然 window.onload 是表示所有的 DOM、css、js、img 等都加载完毕,包括动态生成加载的元素。但是在面对动态生成并加载的dom确实有所不同(推测是在一个时间段内能检测到),例如react的最大组件中(本人的自定义根组件为 App ), componentWillMount 的 performance.now 的时间,大于 window.onload 中 performance.now 的时间,并且 entrys 数组中只有navigation的entry ,和几个 resoruce 类型的entry,而没有用作监控渲染的 paint 类型的 entry 。(尽管此时写有 window.onload 的脚本已经放置于 body 中最下方)。 事实上,直接在 componentDidMount 中,直接获取 entrys 数组,其中也没有 paint 类型的 entry。所以dom挂载完毕,不代表渲染绘制完毕。 毕竟动态创建元素后,将触发的是浏览器的回流和重绘,只有重绘完毕,才算是渲染绘制完毕。

  2. 单页面切换路由,timing不会刷新,依然保持了整个项目最初的检测。因此需要使用navigation的entry处理。

解决方案/思路:

通过轮询监控paint的entry是否加载提供完毕来确定采集结束时机,然后利用first paint和first-contentful-paint进行计算处理(兼容性不够好)。

采集误差

stalled时间,及阻塞暂停时间,多发生在:

  • 同域名tcp链接并发限制(常见)
  • 浏览器正在分配缓存空间
  • 有更高优先级请求正在处理

方案:

  • 上报页面加载开始时间,即fetchStart
  • 上报后续各时间点的相对增量(timeline中的navigation类型的entry已经实现)
  • 进行阶段清洗和异常处理

参考

www.infoq.cn/article/Dxa…

写在最后

需要声明的一点是,我不是一个教授者,我只是一个分享者、一个讨论者、一个学习者,有不同的意见或新的想法,提出来,我们一起研究。分享的同时,并不只是被分享者在学习进步,分享者亦是。

知识遍地,拾到了就是你的。

既然有用,不妨点赞,让更多的人了解、学习并提升。

号外

我写了一个前端基础性能监控的包: github.com/Indifferenc…

npm包下载: npm install performance-kits --save

求星星,等拍砖,orz,咱们结个善缘~