Performance --- 前端性能監控

閱讀目錄javascript

一:什麼是Performance?css

Performance是前端性能監控的API。它能夠檢測頁面中的性能,W3C性能小組引入進來的一個新的API,它能夠檢測到白屏時間、首屏時間、用戶可操做的時間節點,頁面總下載的時間、DNS查詢的時間、TCP連接的時間等。所以咱們下面來學習下這個API。html

那麼在學習以前,前端性能最主要的測試點有以下幾個:前端

白屏時間:從咱們打開網站到有內容渲染出來的時間點。
首屏時間:首屏內容渲染完畢的時間節點。
用戶可操做時間節點:domready觸發節點。
總下載時間:window.onload的觸發節點。vue

咱們如今在html中來簡單的使用下performance的基本代碼:以下代碼所示:java

<!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基本信息以下:react

如上能夠看到,performance包含三個對象,分別爲 memory、navigation、timing. 其中 memory 是和內存相關的,navigation是指來源相關的,也就是說從那個地方跳轉過來的。timing是關鍵點時間。下面咱們來分別介紹下該對象有哪些具體的屬性值。jquery

performance.memory 含義是顯示此刻內存佔用的狀況,從如上圖能夠看到,該對象有三個屬性,分別爲:webpack

jsHeapSizeLimit 該屬性表明的含義是:內存大小的限制。
totalJSHeapSize 表示 總內存的大小。
usedJSHeapSize 表示可以使用的內存的大小。
若是 usedJSHeapSize 大於 totalJSHeapSize的話,那麼就會出現內存泄露的問題,所以是不容許大於該值的。git

performance.navigation 含義是頁面的來源信息,該對象有2個屬性值,分別是:redirectCount 和 type。

redirectCount: 該值的含義是:若是有重定向的話,頁面經過幾回重定向跳轉而來,默認爲0;
type:該值的含義表示的頁面打開的方式。默認爲0. 可取值爲0、一、二、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 = entry.name;
      obj.entryType = entry.entryType;
      obj.initiatorType = entry.initiatorType;
      obj.duration = entry.duration;
      return obj;
    }
    window.onload = function() {
      var entries = window.performance.getEntries();
      console.log(entries);
      entries.forEach(function(item) {
        if (item.initiatorType) {
          var curItem = 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'));

    // 使用 performance.clearMeasures() 方法來清除 自定義添加的 measure
    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上一些代碼。這些不重要,重要的是學到東西,而且在項目中能用起來。提升效率。

相關文章
相關標籤/搜索