前端性能监控(一)performance与lighthouse

178 阅读8分钟

一、监控什么

Google 开发者提出了一种 RAIL 模型来衡量应用性能,即: ResponseAnimationIdleLoad ,分别代表着 web 应用生命周期的四个不同方面

并指出最好的性能指标是:100ms 内响应用户输入;动画或者滚动需在 10ms 内产生下一帧;最大化空闲时间;页面加载时长不超过 5 秒

1. 页面响应速度指标:

image.png

  • FP:首次绘制

    First Paint:标记浏览器渲染任何在视觉上不同于导航前屏幕内容之内容的时间点

  • FCP:首次内容绘制

    First Contentful Paint:标记的是浏览器渲染来自 DOM 第一位内容的时间点,该内容可能是文本、图像、SVG等

  • FMP:首次有效绘制

    First Meaningful Paint:首次有效绘制,标记主角元素渲染完成的时间点,主角元素可以是视频网站的视频控件,内容网站的页面框架也可以是资源网站的头图等

  • TTI:页面可交互时间

    Time to Interactive:页面可交互时间,即从页面开始加载,一直到用户可以自由输入或操作页面的时间

二、监控手段(window.performance)

a. window.performance

1. 属性

  • navigation :返回一个 PerformanceNavigation 对象。这个对象表示出现在当前浏览上下文的 navigation 类型,比如获取某个资源所需要的重定向次数

    • redirectCount:表示在到达这个页面之前重定向了多少次
    • type:表示是如何导航到这个页面的
      • 0:当前页面是通过点击链接,书签和表单提交,或者脚本操作,或者在url中直接输入地址,type值为0
      • 1:点击刷新页面按钮或者通过Location.reload()方法显示的页面
      • 2:页面通过历史记录和前进后退访问时
      • 255:任何其他方式,type值为255
  • timing已废弃 ,使用 window.performance.getEntriesByType('navigation') 来替代,即 PerformanceNavigationTiming

2.timing(window.performance.getEntriesByType('navigation'))

image.png

image.png

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

b. 性能监听

1.普通用法(Performance timeline)

<img id="image0" src="https://www.w3.org/Icons/w3c_main.png" />
<script>
function init() {
  // see [[USER-TIMING-2]]
  performance.mark("startWork");
  setTimeout(() => {
    performance.mark("endWork");
    measurePerf();
  }, 2000);

}
function measurePerf() {
  performance
    .getEntries()
    .map(entry => JSON.stringify(entry, null, 2))
    .forEach(json => console.log(json));
}
具体结果可看:
{
  "name": "",
  "entryType": "navigation",
  "startTime": 0,
  "duration": 50.07500003557652,
}
 {
  "name": "https://www.w3.org/Icons/w3c_main.png",
  "entryType": "resource",
}
 {
  "name": "startWork",
  "entryType": "mark",
  "startTime": 49.990000028628856,
  "duration": 0
}
 {
  "name": "first-paint",  // FP:页面上第一个像素落点的时候
  "entryType": "paint",
  "startTime": 94.83499999623746,
  "duration": 0
}
 {
  "name": "first-contentful-paint", // FCP: 页面上开始有内容绘制的时候、首个可见元素的绘制时间,包括文本、图片、canvas
  "entryType": "paint",
  "startTime": 94.83499999623746,
  "duration": 0
}
 {
  "name": "endWork",
  "entryType": "mark",
  "startTime": 2050.5150000099093,
  "duration": 0
}

2.performanceObserver

PerformanceObserver,是浏览器内部对Performance实现的观察者模式,即: 当有性能数据产生时,主动通知你。

// 定义一个观察者
const observer = new PerformanceObserver(list => {
    list.getEntries().forEach((entry) => {
        console.log('entry对象', entry);
    });
});
// 观察的类型
observer.observe({
    entryTypes: ['paint']
});

关于 entryTypes, 可以取如下值:

  • frame:event-loop 时的每一帧
  • navigation:导航
  • resource:资源
  • mark: 打点,得到一个时间戳
  • measure:在两个点之间测量
  • paint:绘制
  • longtask(好像只有 chrome支持):任何在浏览器中执行超过 50 ms 的任务,都是 long task

关于 entry: 每个事件类型的 entry 对象都不一样。

resource entry 对象里能拿到相关的数据有

image.png

c. 监控工具

  • 谷歌插件:lighthouse,性能分析利器,精准全面

image.png

三、获取各时间指标

指标计算方式说明
页面加载总耗时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 事件触发的开始时间,这时候页面可以交互
首屏时间有两种方式:第一种通过计算首屏区域内的所有图片加载时间,然后取其最大值;第二种方式:通