【一】前端性能监控之请求耗时统计

4,140 阅读8分钟

一、监控哪些指标?

  • 页面性能指标
    • FP:首次绘制
      First Paint:标记浏览器渲染任何在视觉上不同于导航前屏幕内容之内容的时间点
    • FCP:首次内容绘制
      First Contentful Paint:标记的是浏览器渲染来自 DOM 第一位内容的时间点,该内容可能是文本、图像、SVG等
    • FMP:首次有效绘制
      First Meaningful Paint:首次有效绘制,标记主角元素渲染完成的时间点,主角元素可以是视频网站的视频控件,内容网站的页面框架也可以是资源网站的头图等
    • TTI:页面可交互时间
      Time to Interactive:页面可交互时间,即从页面开始加载,一直到用户可以自由输入或操作页面的时间
  • 静态资源(js,css,img)加载时间
  • 动态资源加载时间

二、统计请求耗时的方式

  • Performance API:
    • performance.timing已弃用,使用performance.getEntriesByType("navigation")代替,但是目前依旧是可用的。
    • PerformanceNavigationTiming 提供了用于存储和检索有关浏览器文档事件的指标的方法和属性。例如,此接口可用于确定加载或卸载文档需要多少时间。该属性通过performance.getEntriesByType("navigation")来返回。
  • PerformanceObserver: 用于监测性能度量事件,在浏览器的性能时间轴记录下一个新的performance entries ****的时候将会被通知

三、使用

1. Performance Api 监听静态资源及页面性能

这里主要使用performance.getEntries(),通过这个方法可以获取到所有的 performance 实体对象,通过 getEntriesByName 和 getEntriesByType 方法可对所有的 performance 实体对象 进行过滤,返回特定类型的实体。

performance.getEntriesByType("navigation") & performance.timing

performance接口可以获取到当前页面中与性能相关的信息,以通过调用只读属性 Window.performance 来获得。这里我们主要用得到的是performance.timing属性,但是MDN官网已经显示该属性已经处于废弃状态,故这里我们使用MDN推荐的PerformaceNavigationTiming来代替,该属性可以通过performance.getEntriesByType("navigation")获取:

他们的含义如下:

注:粗体PerformaceNavigationTiming中含有而performance.timing中没有的属性,删除线则反之。

属性名含义
connectStart返回与服务端建立连接开始时间,如果是持久连接或者是从缓存中获取资源,则这个值等于domainLookupEnd
connectEnd返回与服务端建立连接完成时间,如果是持久连接或者是从缓存中获取资源,则这个值等于domainLookupEnd
decodedBodySize返回编码后字节大小
domCompletehtml文档完全解析完毕的时间节点
domContentLoadedEventEndDOMContentLoaded事件触发的结束时间
domContentLoadedEventStartDOMContentLoaded事件触发的结开始时间
domInteractive返回当前网页DOM结构结束解析、开始加载内嵌资源(如js、css)时,即Document.readyState变为interactive的时间
domLoading返回当前网页DOM结构开始解析时,即Document.readyState变为loading时的时间戳
domainLookupEnd返回查询DNS结束时间,如果是持久连接或者是从缓存中获取资源,则这个值等于fetchStart
domainLookupStart返回查询DNS开始时间,如果是持久连接或者是从缓存中获取资源,则这个值等于fetchStart
durationPerformanceNavigationTiming.loadEventEnd (en-US)PerformanceEntry.startTime (en-US) 属性之间的差值
entryType返回 "navigation"
fetchStart如果要使用“GET”请求方法获取新资源,fetchStart返回的是浏览器发起请求到检测缓存前时间,否则直接返回浏览器请求时间
initiatorType返回 "navigation"
loadEventEnd返回onload完成时间,当load事件尚未触发时,它返回零
loadEventStart返回onload开始时间,当load事件尚未触发时,它返回零
name返回页面url
nextHopProtocol网路资源协议
redirectCount重定向数
redirectEnd返回最后一个HTTP重定向完成时间,如果没有重定向,或者重定向中的一个不同源,这个值会返回0
redirectStart返回第一个HTTP重定向开始时间,如果没有重定向,或者重定向中的一个不同源,这个值会返回0
renderBlockingStatus识别页面上的资源是否有渲染阻塞
requestStart返回浏览器向服务器发出HTTP请求时间
responseEnd返回浏览器从服务器收到(或从本地缓存读取,或从本地资源读取)最后一个字节时间(如果在此之前HTTP连接已经关闭,则返回关闭时)的时间
responseStart返回浏览器从服务器收到(或从本地缓存读取)第一个字节时间。如果传输层在开始请求之后失败并且连接被重开,该属性将会被数制成新的请求的相对应的发起时间
responseStatus
secureConnectionStart返回浏览器与服务器开始安全链接的握手时间,如果当前网页不要求安全连接,则返回0
serverTiming返回列表 PerformanceServerTiming
startTime返回开始记录性能时间,0
transferSize响应头和响应体大小
type一个 string 表示导航类型,取值为为:“navigate”,“reload”,“back_forward”或“prerender”
unloadEventEnd返回unload处理完成时间,如果当前url与上一个url是同源,则返回的值是指上一个页面卸载到这个页面用户代理开始前时间,如果与上一个不同域或者没有上一个url,则返回0
unloadEventStart返回unload开始处理时间,如果当前url与上一个url是同源,则返回的值是指上一个页面卸载到这个页面用户代理后时间,如果与上一个不同域或者没有上一个url,则返回0
workerStart如果有 worker,则返回 worker 的开始时间

指标计算方式:

指标计算方式说明
页面加载总耗时loadEventEnd - startTime指页面完全加载完所用的时间,这时候触发完成了 onload 事件
DNS解析耗时domainLookupEnd - domainLookupStart指通过域名解析服务(DNS),将指定的域名解析成IP地址所消耗的时间
TCP连接耗时connectEnd - connectStart指浏览器和WEB服务器建立TCP/IP连接所用的时间
SSL连接耗时location.protocol === 'https:' ? connectEnd - secureConnectionStart只在 HTTPS 下有效,属于TCP连接耗时的一部分,指安全连接握手耗时
网路请求耗时responseStart - requestStart指开始发送请求到服务器返回第一个字节所需要的时间
数据传输耗时responseEnd-responseStart指服务器端返回第一个字节到最后一个字节所需要的时间
DOM解析耗时domContentLoadedEventEnd - responseEnd指页面请求完成(responseEnd)后,到整个 DOM 解析完所用的时间,页面的复杂度决定了 DOM 解析耗时
资源加载耗时loadEventEnd - domContentLoadedEventEnd指 DOM 解析完成后到页面完全加载完所用的时间
首包时间responseStart - startTime指从页面请求到浏览器开始接收到数据所用的时间
页面渲染耗时loadEventEnd - responseEnd等于页面完全加载时间 - HTML 加载完成时间(见下面指标)
页面完全加载时间loadEventEnd - startTime指页面完全加载完所用的时间,这时候触发完成了 onload 事件
白屏时间优先使用最新标准 performance.getEntriesByType('paint')[0].startTime,不支持的话使用 Chrome、IE 提供的 firstPaintTime,chrome.loadTimes().firstPaintTime 或 performance.msFirstPaint,还没有获取,取 domInteractive - startTime但是实际上报取的值是:loadEventEnd - startTime首次渲染时间,指页面出现第一个文字或图像所花费的时间
页面加载完时间loadEventEnd - startTime指页面完全加载完所用的时间,这时候触发完成了 onload 事件
HTML加载完时间responseEnd - startTime指页面所有 HTML 加载完成(不包括页面渲染时间),即包括 DNS、TCP、Request 和 Response
首次交互时间domInteractive - startTime指页面 DOMContentLoaded 事件触发的开始时间,这时候页面可以交互
首屏时间有两种方式:第一种通过计算首屏区域内的所有图片加载时间,然后取其最大值;第二种方式:通过 window.MutationObserver 来监听首屏所有元素变化情况,并记录时间,最后取其最大值(会去掉得分相同重复的值),算出的时间需要加上 domInteractive(可交互时间),目前系统采用第二种实现方式,如果第二种取不到,取 domInteractive,但该值会比实际首屏时间要小首屏时间,也称用户完全可交互时间,即整个页面首屏完全渲染出来,用户完全可以交互,一般首屏时间小于页面完全加载时间,该指标
performance.getEntriesByType("resource"); 监听页面静态资源

这个api可以获取页面某类资源的加载时间,可测量图片、js、css、XHR、fetch等。

注意:如果请求为跨域请求,需要通过配置# Timing-Allow-Origin 来允许浏览器采集性能数据:

Timing-Allow-Origin

2. PerformanceObsever 监听页面动态资源

使用 performance.getEntries() 获取资源的时候,可能并不能一下子拿到页面所有的资源,因为资源请求也不是一次性加载完的,就像懒加载的图片

所以我们还需要监听资源的动态载入

具体我们会使用 PerformanceObserver 对资源进行监听。

示例如下:

const observer = new PerformanceObserver((list, observer) => {
  list.getEntries().forEach((entry) => {
    console.log(`name    : ${entry.name}`);
    console.log(`type    : ${entry.entryType}`);
    console.log(`duration: ${entry.duration}`);
  });
});
observer.observe({
  entryTypes: ["resource"],
});

const xhrRequest = (method, url) => {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open(method, url);
    xhr.send();
  });
};

xhrRequest("get", "http://101.43.155.53:9001/banner");
fetch("http://101.43.155.53:9001/banner", {
  method: "get",
});

四、兼容性

getEntries

PerformanceObserver