https://www.cnblogs.com/tugenhua0707/p/10982332.html 本文转载

Performance --- 前端性能监控

2019-06-05 22:23  龙恩0707  阅读(4429)  评论(1)  编辑  收藏  举报

    Performance --- 前端性能监控
</div>

阅读目录

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

一:什么是Performance?

Performance是前端性能监控的API。它可以检测页面中的性能,W3C性能小组引入进来的一个新的API,它可以检测到白屏时间、首屏时间、用户可操作的时间节点,页面总下载的时间、DNS查询的时间、TCP链接的时间等。因此我们下面来学习下这个API。

那么在学习之前,前端性能最主要的测试点有如下几个:

白屏时间:从我们打开网站到有内容渲染出来的时间点。
首屏时间:首屏内容渲染完毕的时间节点。
用户可操作时间节点:domready触发节点。
总下载时间:window.onload的触发节点。

我们现在在html中来简单的使用下performance的基本代码:如下代码所示:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>performance演示</title>
</head>
<body><script type="text/javascript">var performance = window.performance || window.msPerformance || window.webkitPerformance;if (performance) {console.log(performance);}</script>
</body>
</html>

然后在浏览器下会打印如下 performance基本信息如下:

如上可以看到,performance包含三个对象,分别为 memory、navigation、timing. 其中 memory 是和内存相关的,navigation是指来源相关的,也就是说从那个地方跳转过来的。timing是关键点时间。下面我们来分别介绍下该对象有哪些具体的属性值。

performance.memory 含义是显示此刻内存占用的情况,从如上图可以看到,该对象有三个属性,分别为:

jsHeapSizeLimit 该属性代表的含义是:内存大小的限制。
totalJSHeapSize 表示 总内存的大小。
usedJSHeapSize 表示可使用的内存的大小。
如果 usedJSHeapSize 大于 totalJSHeapSize的话,那么就会出现内存泄露的问题,因此是不允许大于该值的。

performance.navigation 含义是页面的来源信息,该对象有2个属性值,分别是:redirectCount 和 type。

redirectCount: 该值的含义是:如果有重定向的话,页面通过几次重定向跳转而来,默认为0;
type:该值的含义表示的页面打开的方式。默认为0. 可取值为0、1、2、255.

0(TYPE_NAVIGATE):表示正常进入该页面(非刷新、非重定向)。
1(TYPE_RELOAD):表示通过 window.location.reload 刷新的页面。如果我现在刷新下页面后,再来看该值就变成1了。
2(TYPE_BACK_FORWARD ):表示通过浏览器的前进、后退按钮进入的页面。如果我此时先前进下页面,再后退返回到该页面后,查看打印的值,发现变成2了。
255(TYPE_RESERVED): 表示非以上的方式进入页面的。

如下图所示:

performance.onresourcetimingbufferfull; 如上截图也有这个属性的,该属性的含义是在一个回调函数。该回调函数会在浏览器的资源时间性能缓冲区满了的时候会执行的。

performance.timeOrigin:是一系列时间点的基准点,精确到万分之一毫秒。如上截图该值为:1559526951495.139,该值是
一个动态的,刷新下,该值是会发生改变的。

performance.timing:是一系列关键时间点,它包含了网络、解析等一系列的时间数据。

为了方便,从网上弄了一张图片过来,来解析下各个关键时间点的含义如下所示:

按照如上图的顺序,我们来分别看下各个字段的含义如下:

navigationStart: 含义为:同一个浏览器上一个页面卸载结束时的时间戳。如果没有上一个页面的话,那么该值会和fetchStart的值相同。

redirectStart: 该值的含义是第一个http重定向开始的时间戳,如果没有重定向,或者重定向到一个不同源的话,那么该值返回为0.

redirectEnd: 最后一个HTTP重定向完成时的时间戳。如果没有重定向,或者重定向到一个不同的源,该值也返回为0.

fetchStart: 浏览器准备好使用http请求抓取文档的时间(发生在检查本地缓存之前)。

domainLookupStart: DNS域名查询开始的时间,如果使用了本地缓存话,或 持久链接,该值则与fetchStart值相同。

domainLookupEnd: DNS域名查询完成的时间,如果使用了本地缓存话,或 持久链接,该值则与fetchStart值相同。

connectStart: HTTP 开始建立连接的时间,如果是持久链接的话,该值则和fetchStart值相同,如果在传输层发生了错误且需要重新建立连接的话,那么在这里显示的是新建立的链接开始时间。

secureConnectionStart: HTTPS 连接开始的时间,如果不是安全连接,则值为 0

connectEnd:HTTP完成建立连接的时间(完成握手)。如果是持久链接的话,该值则和fetchStart值相同,如果在传输层发生了错误且需要重新建立连接的话,那么在这里显示的是新建立的链接完成时间。

requestStart: http请求读取真实文档开始的时间,包括从本地读取缓存,链接错误重连时。

responseStart: 开始接收到响应的时间(获取到第一个字节的那个时候)。包括从本地读取缓存。

responseEnd: HTTP响应全部接收完成时的时间(获取到最后一个字节)。包括从本地读取缓存。

unloadEventStart: 前一个网页(和当前页面同域)unload的时间戳,如果没有前一个网页或前一个网页是不同的域的话,那么该值为0.

unloadEventEnd: 和 unloadEventStart 相对应,返回是前一个网页unload事件绑定的回调函数执行完毕的时间戳。

domLoading: 开始解析渲染DOM树的时间。

domInteractive: 完成解析DOM树的时间(只是DOM树解析完成,但是并没有开始加载网页的资源)。

domContentLoadedEventStart:DOM解析完成后,网页内资源加载开始的时间。
domContentLoadedEventEnd: DOM解析完成后,网页内资源加载完成的时间。
domComplete: DOM树解析完成,且资源也准备就绪的时间。Document.readyState 变为 complete,并将抛出 readystatechange 相关事件。

loadEventStart: load事件发送给文档。也即load回调函数开始执行的时间,如果没有绑定load事件,则该值为0.
loadEventEnd: load事件的回调函数执行完毕的时间,如果没有绑定load事件,该值为0.

如上就是各个值的含义了,大家简单的看下,了解下就行了,不用过多的折腾。在使用这些值来计算白屏时间、首屏时间、用户可操作的时间节点,页面总下载的时间、DNS查询的时间、TCP链接的时间等之前,我们可以先看下传统方案是如何做的?

传统方案

在该API出现之前,我们想要计算出如上前端性能的话,我们需要使用时间戳来大概估计下要多长时间。比如使用:(new Date()).getTime() 来计算之前和之后的值,然后两个值的差值就是这段的时间已用的时间。但是该方法有误差,不准确。下面我们来看看传统的方案如下:

1.1 白屏时间

白屏时间:是指用户进入该网站(比如刷新页面、跳转到新页面等通过该方式)的时刻开始计算,一直到页面内容显示出来之前的时间节点。如上图我们可以看到,这个过程包括dns查询、建立tcp链接、发送首个http请求等过程、返回html文档。
比如如下代码:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>performance演示</title><script type="text/javascript">var startTime = (new Date()).getTime();</script><link href="xx1.css" rel="stylesheet" /><link href="xx2.css" rel="stylesheet" /><script type="text/javascript" src="xx1.js"></script><script type="text/javascript" src="xx2.js"></script><script type="text/javascript">var endTime = (new Date()).getTime();</script>
</head>
<body><script type="text/javascript">

</script>
</body>
</html>

如上代码,endTime - startTime 的值就可以当作为白屏时间的估值了。

1.2 首屏时间

要获取首屏时间的计算,首先我们要知道页面加载有2种方式:

1. 加载完资源文件后通过js动态获取接口数据,然后数据返回回来渲染内容。

因此会有如下所示的信息图:如下所示:

2. 同构直出前端页面,如下所示:

css资源文件加载时间的计算,我们可以如上图所示:t2-t1 就是所有的css加载的时间。

因此假如我们现在的项目文件代码index.html 代码如下所示:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>performance演示</title>

<script type=“text/javascript”>
// 获取页面开始的时间
var pageStartTime = (new Date()).getTime();
</script>

<link href=“xx1.css” rel=“stylesheet” />
<link href=“xx2.css” rel=“stylesheet” />

<script type=“text/javascript”>
// 获取加载完成后的css的时间
var cssEndTime = (new Date()).getTime();
</script>

<script type=“text/javascript” src=’./jquery.js’></script>

<script type=“text/javascript”>
// 获取加载完 jquery插件的时间
var jsPluginTime = (new Date()).getTime();
</script>

</head>
<body>

<h1>计算时间</h1>
<script type=“text/javascript”>
// 加载js的开始时间
var JsStartTime = (new Date()).getTime();
</script>

<script type=“text/javascript” src=“xx1.js”></script>
<script type=“text/javascript” src=“xx2.js”></script>

<script type=“text/javascript”>
// 加载完成后的js的结束时间
var JsEndTime = (new Date()).getTime();
</script>

</body>
</html>

如上代码,可以获取页面开始的时间 pageStartTime, 加载资源文件css后的时间就是 cssEndTime - pageStartTime,加载jquery插件的时间 jsPluginTime - cssEndTime 了。但是 js的加载时间难道就是 = JsEndTime - JsStartTime 吗?这肯定不是的,因为JS还需要有执行的时间的。比如js内部做了很多dom操作,那么dom操作需要时间的,那么js加载和执行的时间 = JsEndTime - JsStartTime; 吗?这也是不对的,因为浏览器加载资源文件是并行的,执行js文件是串行的。那如果css文件或jquery文件发起http请求后一直没有返回,那么它会阻塞后续js文件的执行的。但是此时此刻js文件加载很早就已经返回了,但是由于服务器原因或网络原因导致css文件加载很慢,所以会堵塞js文件的执行。

因此我们可以总结为:js加载的时间 不等于 JsEndTime - JsStartTime;同理js加载和执行的实际 也不等于 JsEndTime - JsStartTime。正因为有外链css中的http请求,它会堵塞js的执行,因此很多网站会把外链css文件改成内联css文件代码,内联css代码是串行的。比如百度,淘宝官网等。下面我们来看下这两个网站的源码:

百度源码:

我们打开百度搜索页面,然后我们右键查看网页的源码如下:

我们再来看下百度搜索页面的网络css的请求可以看到如下,同域名下根本就没有css外链操作,所有的css代码都是内联的,我们查看网络看到就仅仅有一个css外链请求,并且该css并不是百度内部的css文件,如下所示:

淘宝官网源码:

我们操作如上所示,打开淘宝官网源码查看如下所示:

并且我们查看淘宝官网的网络请求中只看到两个css外链请求,且该两个外链请求并不是淘宝内部的同域名下的css请求,
如下所示:

并且 该css文件名为 new_suggest-min.css 文件,我通过源码搜索下,并没有发现该css文件外链,因此可以肯定的是该css文件是通过js动态加载进去的,如下所示:

切记:如果body下面有多个js文件的话,并且有ajax动态渲染的文件的话,那么尽量让他放在最前面,因为其他的js加载的时候会阻止页面的渲染,导致渲染js一直渲染不了,导致页面的数据会有一段时间是空白的情况。

回到顶部

二:使用 performance.timing 来计算值

performance 对象有一个timing属性,该属性包含很多属性值,我们还是来看看之前的示意图,如下所示:

从上面的示意图我们可以看到:

重定向耗时 = redirectEnd - redirectStart;
DNS查询耗时 = domainLookupEnd - domainLookupStart;
TCP链接耗时 = connectEnd - connectStart;
HTTP请求耗时 = responseEnd - responseStart;
解析dom树耗时 = domComplete - domInteractive;
白屏时间 = responseStart - navigationStart;
DOMready时间 = domContentLoadedEventEnd - navigationStart;
onload时间 = loadEventEnd - navigationStart;

如上就是计算方式,为了方便我们现在可以把他们封装成一个函数,然后把对应的值计算出来,然后我们根据对应的数据值来进行优化即可。

我们这边来封装下该js的计算方式, 代码如下:

function getPerformanceTiming() {var performance = window.performance;if (!performance) {console.log('您的浏览器不支持performance属性');return;}var t = performance.timing;var obj = {timing: performance.timing};// 重定向耗时obj.redirectTime = t.redirectEnd - t.redirectStart;

// DNS查询耗时
obj.lookupDomainTime = t.domainLookupEnd - t.domainLookupStart;

// TCP链接耗时
obj.connectTime = t.connectEnd - t.connectStart;

// HTTP请求耗时
obj.requestTime = t.responseEnd - t.responseStart;

// 解析dom树耗时
obj.domReadyTime = t.domComplete - t.domInteractive;

// 白屏时间耗时
obj.whiteTime = t.responseStart - t.navigationStart;

// DOMready时间
obj.domLoadTime = t.domContentLoadedEventEnd - t.navigationStart;

// 页面加载完成的时间 即:onload时间
obj.loadTime = t.loadEventEnd - t.navigationStart;

return obj;
}
var obj = getPerformanceTiming();
console.log(obj);

回到顶部

三:前端性能如何优化?

1. 在网页中,css资源文件尽量内联,不要外链,具体原因上面已经有说明。

2. 重定向优化,重定向有301(永久重定向)、302(临时重定向)、304(Not Modified)。前面两种重定向尽量避免。304是用来做缓存的。重定向会耗时。

3. DNS优化,如何优化呢?一般有两点:第一个就是减少DNS的请求次数,第二个就是进行DNS的预获取(Prefetching)。在PC端正常的解析DNS一次需要耗费 20-120毫秒的时间。因此我们可以减少DNS解析的次数,进而就会减少DNS解析的时间。
第二个就是DNS的预获取,什么叫预获取呢?DNS预获取就是浏览器试图在用户访问链接之前解析域名。比如说我们网站中有很多链接,但是这些链接不在同一个域名下,我们可以在浏览器加载的时候就先解析该域名,当用户真正去点击该预解析的该链接的时候,可以平均减少200毫秒的耗时(是指第一次访问该域名的时候,没有缓存的情况下)。这样就能减少用户的等待时间,提高用户体验。
我们下面可以看下淘宝的官网的代码如下就进行了DNS的Prefetch了,如下所示:

DNS Prefetch 应该尽量的放在网页的前面,推荐放在 <meta charset="UTF-8"> 后面。具体使用方法如下:

<link rel="dns-prefetch" href="//xxx.abc.com">
<link rel="dns-prefetch" href="//yyy.def.com">
<link rel="dns-prefetch" href="//bdimg.share.zhix.net">

4. TCP请求优化

TCP的优化就是减少HTTP的请求数量。比如前端资源合并,图片,资源文件进行压缩等这些事情。

在http1.0当中默认使用短链接,也就是客户端和服务端进行一次http请求的时候,就会建立一次链接,任务结束后就会中断该链接。那么在这个过程当中会有3次TCP请求握手和4次TCP请求释放操作。

在http1.1中,在http响应头会加上 Connection: keep-alive,该代码的含义是:当该网页打开完成之后,链接不会马上关闭,
当我们再次访问该链接的时候,会继续使用这个长连接。这样就减少了TCP的握手次数和释放次数。只需要建立一次TCP链接即可,比如我们看下百度的请求如下所示:

5. 渲染优化

在我们做vue或react项目时,我们常见的模板页面是通过js来进行渲染的。而不是同构直出的html页面,对于这个渲染过程中对于我们首屏就会有很大的损耗,白屏的时间会增加。因此我们可以使用同构直出的方式来进行服务器端渲染html页面会比较好,或者我们可以使用一些webpack工具进行html同构直出渲染,webpack渲染可以看这篇文章。

回到顶部

四:Performance中方法

首先我们在控制台中打印下 performance 中有哪些方法,如下代码:

var performance = window.performance;
console.log(performance);

如下所示:

4.1 performance.getEntries()

该方法包含了所有静态资源的数组列表。

比如我现在html代码如下:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>performance演示</title>
</head>
<body><h1>计算时间</h1><img src="http://img.alicdn.com/tps/TB1EMhjIpXXXXaPXVXXXXXXXXXX.jpg" /><script type="text/javascript">window.onload = function() {var performance = window.performance;console.log(performance);console.log(performance.getEntries())}</script>
</body>
</html>

如上代码,我页面上包含一张淘宝cdn上的一张图片,因为该方法会获取页面中所有包含了页面中的 HTTP 请求。
然后我们在浏览器中看到打印performance.getEntries()信息如下:

该对象的属性中除了包含资源加载时间,还有如下几个常见的属性:

name: 资源名称,是资源的绝对路径,如上图就是淘宝cdn上面的图片路径。我们可以通过 performance.getEntriesByName(name属性值),来获取该资源加载的具体属性。

startTime: 开始时间
duration: 表示加载时间,它是一个毫秒数字,只能获取同域下的时间点,如果是跨域的话,那么该时间点为0。
entryType: 资源类型 "resource", 还有 "navigation", "mark" 和 "measure" 这三种。
initiatorType: 表示请求的来源的标签,比如 link标签、script标签、img标签等。

因此我们可以像 getPerformanceTiming 该方法一样,封装一个方法,获取某个资源的时间。如下封装代码方法如下:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>performance演示</title>
</head>
<body><h1>计算时间</h1><img src="http://img.alicdn.com/tps/TB1EMhjIpXXXXaPXVXXXXXXXXXX.jpg" /><script type="text/javascript">// 计算加载时间function getEntryTiming (entry) {var obj = {};// 获取重定向的时间obj.redirectTime = entry.redirectEnd - entry.redirectStart;// 获取DNS查询耗时obj.lookupDomainTime = entry.domainLookupEnd - entry.domainLookupStart;// 获取TCP链接耗时obj.connectTime = entry.connectEnd - entry.connectStart;// HTTP请求耗时obj.requestTime = entry.responseEnd - entry.responseStart;
  obj.name </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> entry.name;obj.entryType </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> entry.entryType;obj.initiatorType </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> entry.initiatorType;obj.duration </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> entry.duration;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">return</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> obj;
}
window.onload </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span> <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">function</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">() {</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">var</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> entries </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> window.performance.getEntries();console.log(entries);entries.forEach(</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">function</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">(item) {</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">if</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> (item.initiatorType) {</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">var</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> curItem </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> getEntryTiming(item);console.log(curItem);}});
}

</script>
</body>
</html>

然后如上会有2个console.log 打印数据,我们到控制台中看到打印信息如下:

如上图所示,我们可以看到通过 getEntryTiming 方法计算后,会拿到各对应的值。

4.2 performance.now()
该方法会返回一个当前页面执行的时间的时间戳,可以用来精确计算程序执行的实际。
比如如下,我循环100万次,然后返回一个数组,我们来看下代码如下:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>performance演示</title>
</head>
<body><h1>计算时间</h1><img src="http://img.alicdn.com/tps/TB1EMhjIpXXXXaPXVXXXXXXXXXX.jpg" /><script type="text/javascript">function doFunc() {var arrs = [];for (var i = 0; i < 1000000; i++) {arrs.push({'label': i,'value': i});}return arrs;}var t1 = window.performance.now();console.log(t1);doFunc();var t2 = window.performance.now();console.log(t2);console.log('doFunc函数执行的时间为:'+ (t2 - t1) + '毫秒');</script>
</body>
</html>

然后我们再来打印下 doFunc() 这个函数执行了多久,如下所示:

我们也知道我们还有一个时间就是 Date.now(), 但是performance.now()与Date.now()方法不同的是:
该方法使用了一个浮点数,返回的是以毫秒为单位,小数点精确到微妙级别的时间。相对于Date.now() 更精确,并且不会受系统程序堵塞的影响。
下面我们来看看使用 Date.now()方法使用的demo如下:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>performance演示</title>
</head>
<body><h1>计算时间</h1><img src="http://img.alicdn.com/tps/TB1EMhjIpXXXXaPXVXXXXXXXXXX.jpg" /><script type="text/javascript">function doFunc() {var arrs = [];for (var i = 0; i < 1000000; i++) {arrs.push({'label': i,'value': i});}return arrs;}var t1 = Date.now();console.log(t1);doFunc();var t2 = Date.now();console.log(t2);console.log('doFunc函数执行的时间为:'+ (t2 - t1) + '毫秒');</script>
</body>
</html>

执行的结果如下:

注意:performance.timing.navigationStart + performance.now() 约等于Date.now();

4.3 performance.mark()

该方法的含义是用来自定义添加标记的时间, 方便我们计算程序的运行耗时。该方法使用如下:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>performance演示</title>
</head>
<body><h1>计算时间</h1><img src="http://img.alicdn.com/tps/TB1EMhjIpXXXXaPXVXXXXXXXXXX.jpg" /><script type="text/javascript">function doFunc() {var arrs = [];for (var i = 0; i < 1000000; i++) {arrs.push({'label': i,'value': i});}return arrs;}// 函数执行前做个标记var mStart = 'mStart';var mEnd = 'mEnd'; window.performance.mark(mStart);doFunc();// 函数执行之后再做个标记window.performance.mark(mEnd);// 然后测量这两个标记之间的距离,并且保存起来var name = 'myMeasure';window.performance.measure(name, mStart, mEnd);// 下面我们通过 performance.getEntriesByName 方法来获取该值console.log(performance.getEntriesByName('myMeasure'));console.log(performance.getEntriesByType('measure'));</script>
</body>
</html>

如上代码,我们通过 window.performance.measure(name, mStart, mEnd); 这个方法做出标记后,我们可以使用performance.getEntriesByName('myMeasure') 和 performance.getEntriesByType('measure') 获取该值。

如下图所示:

4.4 performance.getEntriesByType()

该方法返回一个 PerformanceEntry 对象的列表,基于给定的 entry type, 如上代码 performance.getEntriesByType('measure') 就可以获取该值。

4.5 performance.clearMeasures()

从浏览器的性能输入缓冲区中移除自定义添加的 measure. 代码如下所示:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>performance演示</title>
</head>
<body><h1>计算时间</h1><img src="http://img.alicdn.com/tps/TB1EMhjIpXXXXaPXVXXXXXXXXXX.jpg" /><script type="text/javascript">function doFunc() {var arrs = [];for (var i = 0; i < 1000000; i++) {arrs.push({'label': i,'value': i});}return arrs;}// 函数执行前做个标记var mStart = 'mStart';var mEnd = 'mEnd'; window.performance.mark(mStart);doFunc();// 函数执行之后再做个标记window.performance.mark(mEnd);// 然后测量这两个标记之间的距离,并且保存起来var name = 'myMeasure';window.performance.measure(name, mStart, mEnd);// 下面我们通过 performance.getEntriesByName 方法来获取该值console.log(performance.getEntriesByName('myMeasure'));console.log(performance.getEntriesByType('measure'));
</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> 使用 performance.clearMeasures() 方法来清除 自定义添加的 measure</span>

performance.clearMeasures();
console.log(performance.getEntriesByType(‘measure’));

</script>
</body>
</html>

如上我们在最后代码中使用 performance.clearMeasures() 方法清除了所有自定义的measure。然后我们后面重新使用 console.log(performance.getEntriesByType('measure'));打印下,看到如下信息:

4.6 performance.getEntriesByName(name属性的值)

该方法返回一个 PerformanceEntry 对象的列表,基于给定的 name 和 entry type。

4.7 performance.toJSON()

该方法是一个 JSON 格式转化器,返回 Performance 对象的 JSON 对象。如下代码所示:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>performance演示</title>
</head>
<body><h1>计算时间</h1><img src="http://img.alicdn.com/tps/TB1EMhjIpXXXXaPXVXXXXXXXXXX.jpg" /><script type="text/javascript">console.log(window.performance);var js = window.performance.toJSON();console.log("json = " + JSON.stringify(js));</script>
</body>
</html>

然后打印信息如下:

回到顶部

五:使用performane编写小工具

首先html使用代码如下(切记一定要把初始代码放到window.onload里面,因为确保图片加载完成):

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>performance演示</title>
</head>
<body><h1>计算时间</h1><img src="http://img.alicdn.com/tps/TB1EMhjIpXXXXaPXVXXXXXXXXXX.jpg" /><img src="https://aecpm.alicdn.com/simba/img/TB1XotJXQfb_uJkSnhJSuvdDVXa.jpg" /><script type="text/javascript" src="./js/performance.js"></script><script type="text/javascript">window.onload = function() {window.performanceTool.getPerformanceTiming();};</script>
</body>
</html>

然后页面进行预览效果如下:

如上图我们就可以很清晰的可以看到,页面的基本信息了,比如:重定向耗时、Appcache耗时、DNS查询耗时、TCP链接耗时、HTTP请求耗时、请求完毕到DOM加载耗时、解析DOM树耗时、白屏时间耗时、load事件耗时、及 页面加载完成的时间。页面加载完成的时间 是上面所有时间的总和。及下面,我们也可以分别清晰的看到,js、css、image、video、等信息的资源加载及总共用了多少时间。

js基本代码如下:

src/utils.js 代码如下:

export function isObject(obj) {return obj !== null && (typeof obj === 'object')
}

// 格式化成毫秒
export function formatMs(time) {
if (typeof time !== ‘number’) {
console.log(‘时间必须为数字’);
return;
}
// 毫秒转换成秒 返回
if (time > 1000) {
return (time / 1000).toFixed(2) + ‘s’;
}
// 默认返回毫秒
return Math.round(time) + ‘ms’;
}

export function isImg(param){
if (/.(gif|jpg|jpeg|png|webp|svg)/i.test(param)) {
return true;
}
return false;
}

export function isJS(param){
if (/.(js)/i.test(param)) {
return true;
}
return false;
}

export function isCss(param){
if (/.(css)/i.test(param)) {
return true;
}
return false;
}

export function isVideo(param){
if (/.(mp4|rm|rmvb|mkv|avi|flv|ogv|webm)/i.test(name)) {
return true;
}
return false;
}

export function checkResourceType(param){
if (isImg(param)) {
return ‘image’;
}
if (isJS(param)) {
return ‘javascript’;
}
if (isCss(param)) {
return ‘css’;
}
if (isVideo(param)) {
return ‘video’;
}
return ‘other’
}

js/index.js 代码如下:

var utils = require('./utils');
var formatMs = utils.formatMs;
var isObject = utils.isObject;
var checkResourceType = utils.checkResourceType;

function Performance() {};

Performance.prototype = {
// 获取数据信息
getPerformanceTiming: function() {
// 初始化数据
this.init();
if (!isObject(this.timing)) {
console.log(‘值需要是一个对象类型’);
return;
}
// 过早获取 loadEventEnd值会是0
var loadTime = this.timing.loadEventEnd - this.timing.navigationStart;
if (loadTime < 0) {
setTimeout(() => {
this.getPerformanceTiming();
}, 200);
return;
}
// 获取解析后的数据
this.afterDatas.timingFormat = this._setTiming(loadTime);
this.afterDatas.enteriesResouceDataFormat = this._setEnteries();
this._show();
},
init: function() {
this.timing = window.performance.timing;
// 获取资源类型为 resource的所有数据
this.enteriesResouceData = window.performance.getEntriesByType(‘resource’);
},
// 保存原始数据
timing: {},
// 原始enteries数据
enteriesResouceData: [],
// 保存解析后的数据
afterDatas: {
timingFormat: {},
enteriesResouceDataFormat: {},
enteriesResouceDataTiming: {
“js”: 0,
“css”: 0,
“image”: 0,
“video”: 0,
“others”: 0
}
},
_setTiming: function(loadTime) {
var timing = this.timing;
// 对数据进行计算
var data = {
“重定向耗时”: formatMs(timing.redirectEnd - timing.redirectStart),
“Appcache耗时”: formatMs(timing.domainLookupStart - timing.fetchStart),
“DNS查询耗时”: formatMs(timing.domainLookupEnd - timing.domainLookupStart),
“TCP链接耗时”: formatMs(timing.connectEnd - timing.connectStart),
“HTTP请求耗时”: formatMs(timing.responseEnd - timing.responseStart),
“请求完毕到DOM加载耗时”: formatMs(timing.domInteractive - timing.responseEnd),
“解析DOM树耗时”: formatMs(timing.domComplete - timing.domInteractive),
“白屏时间耗时”: formatMs(timing.responseStart - timing.navigationStart),
“load事件耗时”: formatMs(timing.loadEventEnd - timing.loadEventStart),
“页面加载完成的时间”: formatMs(loadTime)
};
return data;
},
_setEnteries: function() {
var enteriesResouceData = this.enteriesResouceData;
var imageArrs = [],
jsArrs = [],
cssArrs = [],
videoArrs = [],
otherArrs = [];
enteriesResouceData.map(item => {
var d = {
‘资源名称’: item.name,
‘HTTP协议类型’ : item.nextHopProtocol,
“TCP链接耗时” : formatMs(item.connectEnd - item.connectStart),
“加载时间” : formatMs(item.duration)
};
switch(checkResourceType(item.name)) {
case ‘image’:
this.afterDatas.enteriesResouceDataTiming.image += item.duration;
imageArrs.push(d);
break;
case ‘javascript’:
this.afterDatas.enteriesResouceDataTiming.js += item.duration;
jsArrs.push(d);
break;
case ‘css’:
this.afterDatas.enteriesResouceDataTiming.css += item.duration;
cssArrs.push(d);
break;
case ‘video’:
this.afterDatas.enteriesResouceDataTiming.video += item.duration;
videoArrs.push(d);
break;
case ‘others’:
this.afterDatas.enteriesResouceDataTiming.others += item.duration;
otherArrs.push(d);
break;
}
});
return {
‘js’: jsArrs,
‘css’: cssArrs,
‘image’: imageArrs,
‘video’: videoArrs,
‘others’: otherArrs
}
},
_show: function() {
console.table(this.afterDatas.timingFormat);
for( var key in this.afterDatas.enteriesResouceDataFormat ){
console.group(key + “— 共加载时间” + formatMs(this.afterDatas.enteriesResouceDataTiming[key]));
console.table(this.afterDatas.enteriesResouceDataFormat[key]);
console.groupEnd(key);
}
}
};

var Per = new Performance();
module.exports = Per;

github 源码查看

注意:把github源码下载完成后,需要 执行 npm run build 打包,打包完成后,把dist/下的js文件复制到自己项目中即可,然后在项目中引入该js文件,然后调用即可在控制台中看到一些信息效果。

注:基本代码也参考了github上一些代码。这些不重要,重要的是学到东西,并且在项目中能用起来。提高效率。

好文要顶 关注我 收藏该文

龙恩0707
关注 - 22
粉丝 - 1463

+加关注

3
0

<div class="clear"></div>
<div id="post_next_prev"><a href="https://www.cnblogs.com/tugenhua0707/p/10945934.html" class="p_n_p_prefix">« </a> 上一篇:    <a href="https://www.cnblogs.com/tugenhua0707/p/10945934.html" title="发布于 2019-05-29 20:17">nginx开启HSTS让浏览器强制跳转HTTPS访问</a>
<br>
<a href="https://www.cnblogs.com/tugenhua0707/p/10991363.html" class="p_n_p_prefix">» </a> 下一篇:    <a href="https://www.cnblogs.com/tugenhua0707/p/10991363.html" title="发布于 2019-06-08 18:49">理解serverless无服务架构原理(一)</a>
  • 分类 性能相关的
  • 标签 Performance --- 前端性能监控
  • 刷新评论 刷新页面 返回顶部
    登录后才能查看或发表评论,立即 登录 或者 逛逛 博客园首页
    【推荐】百度智能云超值优惠:新用户首购云服务器1核1G低至69元/年
    【推荐】跨平台组态\工控\仿真\CAD 50万行C++源码全开放免费下载!
    【推荐】阿里云云大使特惠:新用户购ECS服务器1核2G最低价87元/年
    【推荐】和开发者在一起:华为开发者社区,入驻博客园科技品牌专区
    【推广】园子与爱卡汽车爱宝险合作,随手就可以买一份的百万医疗保险
    编辑推荐:

    · 理解ASP.NET Core - Host

    · 妙用 background 实现花式文字效果

    · Go 并发编程 – 正确使用 goroutine

    · 前端瓦片地图加载之塞尔达传说旷野之息

    · 技术管理进阶 —— 关于成本优化与利益分配机制

    最新新闻
    · 宇航员回家后身体到底有什么变化,你可能想知道的几个关键点(2021-09-18 14:00)
    · 因加量降价,iPhone13系列几秒售罄,国内高端机市场或加速洗牌(2021-09-18 13:48)
    · 铁打的星巴克,流水的新饮品(2021-09-18 13:35)
    · 奠定时间晶体发现,哈佛、斯坦福、东大、UCB研究员获2022年新视野奖!(2021-09-18 13:20)
    · 2021 AI出行赛道的冲刺时刻(2021-09-18 13:08)
    » 更多新闻...
        </div><script type="text/javascript">var m = window.__blog.contentRendered;if (m) { m(__$("content")); }</script><div id="sidebar"><div id="about"><div><h2 id="about_title">About</h2><div id="about_body"><div id="sidebar_news">
    

    所在地:浙江- 杭州
    姓名:涂根华
    微信号:t879083421
    所在职位:前端攻城师
    兴趣爱好:web前端、旅游等.
    人生格言:路漫漫其修远兮 吾将上下而求索~~
    写文章宗旨:可以理解为2个关键字:细和易,详细的细,尽量保证每篇文章非常的详细,容易的易,且保证每篇文章更通俗易懂, 更容易透彻的理解,更深入的理解。不管你是前端大牛,还是前端小白或想要学习这块知识点的,都希望能看懂和理解透~
    近期目标:做最专业的博客,分享更多的知识点~ 供大家一起学习~ 一起做好分享~
    阿里巴巴的理念是:让全世界没有难做的生意,我现在的公司(铜板街)的理念是:让钱变得更有价值,我写文章的理念:做世界上最专业的博客。淘宝和其他的行业的区别是:种类多,价格相对便宜,京东的区别是:物流快,货比较真实。拼多多的区别是:价格很便宜。我们公司的区别是:到账快,理财种类多,很智能。而我自己定义了两个关键字:易和细,容易的易和详细的细,尽量保证每篇文章非常的详细和通俗易懂,这是我最近一直学习及写文章的宗旨。
    昵称: 龙恩0707
    园龄: 8年
    粉丝: 1463
    关注: 22

    +加关注

    最新随笔

    • 理解 RESTful API 设计规范
    • 了解Unicode编码
    • 理解Javascript执行过程
    • Jenkins 实现前端自动打包,自动部署代码及邮件提醒功能
    • 前端工作流规范
    • vue系列---Mustache.js模板引擎介绍及源码解析(十)
    • vue系列---snabbdom.js使用及源码分析(九)
    • vue系列---Vue组件化的实现原理(八)
    • vue系列--- 认识Flow(一)
    • vue系列---理解Vue中的computed,watch,methods的区别及源码实现(六)

    随笔档案

    • 2020年1月(1)
    • 2019年12月(2)
    • 2019年11月(2)
    • 2019年10月(10)
    • 2019年8月(10)
    • 2019年7月(12)
    • 2019年6月(9)
    • 2019年5月(11)
    • 2019年4月(17)
    • 2019年3月(6)
    • 2019年2月(1)
    • 2019年1月(9)
    • 2018年12月(4)
    • 2018年11月(9)
    • 2018年10月(10)
    • 2018年9月(8)
    • 2018年8月(10)
    • 2018年7月(9)
    • 2018年6月(10)
    • 2018年5月(5)
    • 2018年4月(9)
    • 2018年3月(7)
    • 2018年2月(3)
    • 2018年1月(5)
    • 2017年12月(9)
    • 2017年11月(1)
    • 2017年10月(5)
    • 2017年9月(9)
    • 2017年8月(10)
    • 2017年7月(3)
    • 2017年6月(2)
    • 2017年5月(11)
    • 2017年4月(2)
    • 2016年6月(3)
    • 2016年5月(2)
    • 2016年4月(2)
    • 2016年3月(3)
    • 2016年2月(3)
    • 2016年1月(2)
    • 2015年12月(4)
    • 2015年11月(1)
    • 2015年10月(1)
    • 2015年9月(3)
    • 2015年8月(5)
    • 2015年7月(9)
    • 2015年6月(6)
    • 2015年5月(11)
    • 2015年4月(9)
    • 2015年3月(8)
    • 2015年2月(2)
    • 2015年1月(2)
    • 2014年12月(4)
    • 2014年11月(6)
    • 2014年10月(2)
    • 2014年9月(5)
    • 2014年8月(3)
    • 2014年7月(6)
    • 2014年6月(5)
    • 2014年5月(3)
    • 2014年4月(5)
    • 2014年3月(2)
    • 2014年2月(2)
    • 2014年1月(13)
    • 2013年12月(14)
    • 2013年11月(7)
    • 2013年10月(5)
    • 2013年9月(2)
    • 2013年8月(10)
    • 更多

    文章档案

    • 2017年5月(1)

    积分与排名

    • 积分 - 955793
    • 排名 - 256

    推荐排行榜

    • 1. Git使用教程(187)
    • 2. SVN使用教程总结(62)
    • 3. Fiddler调式使用知多少(一)深入研究(54)
    • 4. Javascript事件总结(29)
    • 5. Fiddler实战深入研究(二)(28)

    随笔分类

    • CSS(9)
    • CSS3(14)
    • Ecmascript 6(3)
    • egg(3)
    • Electron相关的(6)
    • ES6(10)
    • HTML(1)
    • HTML5(25)
    • javascript(110)
    • javascript设计模式的理解(4)
    • javascript数据结构与算法(9)
    • javascript正则表达式(2)
    • JS底层知识(2)
    • koa2(7)
    • Mongodb(13)
    • mysql(9)
    • nginx相关的(9)
    • nodeJS(21)
    • PM2(1)
    • ReactJS(3)
    • Redis(3)
    • requireJS(2)
    • RESTful API 设计规范(1)
    • Serverless(3)
    • service worker(7)
    • SSR服务器端渲染(1)
    • SVG(3)
    • vue(21)
    • web workers(1)
    • webpack(27)
    • websocket(7)
    • web安全相关的(4)
    • 缓存相关的(2)
    • 前端测试框架(2)
    • 前端工作流规范(2)
    • 前端项目工具(28)
    • 深入Vue技术栈及源码系列 (10)
    • 性能相关的(1)
    • 源码分析(9)

    阅读排行榜

    • 1. JS日期格式化转换方法(401425)
    • 2. 理解CSS3中的background-size(对响应性图片等比例缩放)(165982)
    • 3. 理解Vue中的Render渲染函数(123498)
    • 4. 理解webpack之process.env.NODE_ENV详解(十八)(120926)
    • 5. POST提交数据之---Content-Type的理解;(109052)
    • 6. Git使用教程(99508)
    • 7. Javascript设计模式详解(82047)
    • 8. 理解WebSocket心跳及重连机制(五)(81526)
    • 9. 理解Vuex的辅助函数mapState, mapActions, mapMutations用法(80033)
    • 10. MongoDB可视化工具--Robo 3T 使用教程(78783)
    • 11. 理解vue中的scope的使用(73200)
    • 12. css3 实现图片等比例放大与缩小(70630)
    • 13. 30分钟手把手教你学webpack实战(69875)
    • 14. web安全之XSS攻击原理及防范(69382)
    • 15. Nginx中的Rewrite的重定向配置与实践(67557)
    • 16. HTTP请求中 request payload 和 formData 区别?(65815)
    • 17. Echarts 柱状图配置详解(60211)
    • 18. nodeJS-使用buffer类处理二进制数据(48611)
    • 19. scp传输文件的命令(47646)
    • 20. javascript和HTML5上传图片之前实现预览效果(43272)

    Copyright © 2021 龙恩0707
    Powered by .NET 6 on Kubernetes

    博客园

        </div>
    </div>
    

performance 优化相关推荐

  1. ios性能分析和优化

    http://game.ceeger.com/Manual/iphone-InternalProfiler.html http://game.ceeger.com/Manual/Profiler.ht ...

  2. 微软宣布推出Windows Embedded Compact 2013正式版

    Microsoft announces general availability of Windows Embedded Compact 2013 微软宣布推出Windows Embedded Com ...

  3. ARM 之七 主流编译器(armcc、iar、gcc for arm、LLVM(clang))详细介绍

    必备   在讲解各编译器之前,必须先了解一下以下这些文件.这些文件在编译器目录下或者编译生成目标平台的可执行程序时经常见到.此外,还需要注意区分 Windows 平台 和 Linux 平台的文件. . ...

  4. logon dialog 的弹出逻辑debug出来了,有很多有用的代码片段

    Sent: Tuesday, 20 October, 2015 8:02 PM 这两天在做UI端的performance优化,昨天Ross发现在My Opportunity Application初始 ...

  5. 【转】ARM 之七 主流编译器(armcc、iar、gcc for arm、LLVM(clang))详细介绍

    转自:ARM 之七 主流编译器(armcc.iar.gcc for arm.LLVM(clang))详细介绍_itexp-CSDN博客_armcc 必备   在讲解各编译器之前,必须先了解一下以下文件 ...

  6. TCP/IP网络编程 - 基础学习

    1. 创建Socket #include<sys/types.h> #include<sys/socket.h>int sock = ::socket(PF_INET, SOC ...

  7. 3dmax应用领域_3D打印的应用领域是什么?

    3dmax应用领域 什么是3D打印? (What is 3D printing?) 3D printing is a new way of manufacturing solid objects ba ...

  8. commit 提交规范

    commit 格式 <type>(<scope>) : <subject> <空行> <body> <空行> <foote ...

  9. Unity 之 Profiler概述

    Profiler overview Unity 官方说明文档及翻译: The Unity Profiler Window helps you to optimize your game. It rep ...

最新文章

  1. 强烈安利!这个私藏已久的神器!
  2. 开源WebGIS实施方案(一):开篇 [转]
  3. nginx实现防止ddos攻击
  4. linux shell 内建命令,什么是Bash Shell的内建(build in)命令
  5. Spring Boot 快速集成第三方登录功能
  6. 安装oracle ora-01005,Exteernal table ORA-29913,ORA-30653,KUP-01005
  7. php mysql刷新表格_PHP和AJAMYSQL数据库刷新表格
  8. 丰巢回应小学生用照片“刷脸”取件;苹果明年或发布四款 5G 手机;Spring Boot 2.2.0 发布 | 极客头条...
  9. 计算机一级演示文稿知识点,计算机一级考试ppt演示文稿及上网题考点
  10. 杨辉三角(C语言简单版)
  11. python菱形图案_「每日一练」巧用python输出菱形图案
  12. PnPUtil (PnPUtil.exe) 是一个命令行工具,使管理员可以执行以下操作驱动程序包
  13. 无人驾驶--实时定位与地图构建(SLAM)仿真与实战(附源码)
  14. 电源上的sense什么意思_开关电源基本术语
  15. 技术人员如何判断靠谱的创业合伙人?
  16. 常用的电平转换方案(74HC245、74LVC4245等)
  17. python爬虫之常见的加密方式
  18. AR增强现实在现实生活中的应用
  19. docker 容器 磁盘 10G 限制 大小
  20. 幂级数求和难吗?细节很重要

热门文章

  1. C#datagridview中双缓存Dgv
  2. 2022 年最佳 15 款监控工具!你不可错过
  3. joycdr2html.exe系统错误,win7安装软件CDR2020 弹出错误1719无法访问Windws Installer服务解决方案...
  4. 证明最小码距与纠检错图像_最小码距和检错纠错能力关系
  5. mgo 多条件联合查询
  6. NETDMIS5.0端面圆跳动2023
  7. Linux shell 交互式编程、TCL/TK 和 Expect 编译与安装、expect 编程
  8. The value of the property 'type' cannot be parsed
  9. AI红包皮速领,人类现金速抽|祝大家新春快乐
  10. 新浪微博搜索 s.weibo.com [已失效]