前端性能監控之performance


如果我們想要對一個網頁進行性能監控,那么使用window.performance是一個比較好的選擇。

我們通過window.performance可以獲取到用戶訪問一個頁面的每個階段的精確時間,從而對性能進行分析。

 

一、頁面性能監控

1、利用performance.timing來監控網頁的性能

網頁的整個生命周期

PerformanceTiming屬性如下

PerformanceTiming.navigationStart 
表示從同一個瀏覽器上下文的上一個文檔卸載(unload)結束時的UNIX時間戳。如果沒有上一個文檔,這個值會和PerformanceTiming.fetchStart相同。

PerformanceTiming.unloadEventStart 
表示unload事件拋出時的UNIX時間戳。如果沒有上一個文檔,或者,如果上一個文檔或所需的重定向之一不是來自同一個源, 這個值會返回0.

PerformanceTiming.unloadEventEnd 
表示unload事件處理完成時的UNIX時間戳。如果沒有上一個文檔,或者,如果上一個文檔或所需的重定向之一不是來自同一個源, 這個值會返回0.

PerformanceTiming.redirectStart 
表示第一個HTTP重定向開始時的UNIX時間戳。如果沒有重定向,或者重定向中的一個不同源,這個值會返回0.

PerformanceTiming.redirectEnd 
表示最后一個HTTP重定向完成時(也就是說是HTTP響應的最后一個比特直接被收到的時間)的UNIX時間戳。如果沒有重定向,或者重定向中的一個不同源,這個值會返回0.

PerformanceTiming.fetchStart 
表示瀏覽器准備好使用HTTP請求來獲取(fetch)文檔的UNIX時間戳。這個時間點會在檢查任何應用緩存之前。

PerformanceTiming.domainLookupStart 
表示域名查詢開始的UNIX時間戳。如果使用了持續連接(persistent connection),或者這個信息存儲到了緩存或者本地資源上,這個值將和 PerformanceTiming.fetchStart一致。

PerformanceTiming.domainLookupEnd 
表示域名查詢結束的UNIX時間戳。如果使用了持續連接(persistent connection),或者這個信息存儲到了緩存或者本地資源上,這個值將和 PerformanceTiming.fetchStart一致。

PerformanceTiming.connectStart 
返回HTTP請求開始向服務器發送時的Unix毫秒時間戳。如果使用持久連接(persistent connection),則返回值等同於fetchStart屬性的值。

PerformanceTiming.connectEnd 
返回瀏覽器與服務器之間的連接建立時的Unix毫秒時間戳。如果建立的是持久連接,則返回值等同於fetchStart屬性的值。連接建立指的是所有握手和認證過程全部結束。

PerformanceTiming.secureConnectionStart 
返回瀏覽器與服務器開始安全鏈接的握手時的Unix毫秒時間戳。如果當前網頁不要求安全連接,則返回0。

PerformanceTiming.requestStart 
返回瀏覽器向服務器發出HTTP請求時(或開始讀取本地緩存時)的Unix毫秒時間戳。

PerformanceTiming.responseStart 
返回瀏覽器從服務器收到(或從本地緩存讀取)第一個字節時的Unix毫秒時間戳。如果傳輸層在開始請求之后失敗並且連接被重開,該屬性將會被數制成新的請求的相對應的發起時間。

PerformanceTiming.responseEnd 
返回瀏覽器從服務器收到(或從本地緩存讀取,或從本地資源讀取)最后一個字節時(如果在此之前HTTP連接已經關閉,則返回關閉時)的Unix毫秒時間戳。

PerformanceTiming.domLoading 
返回當前網頁DOM結構開始解析時(即Document.readyState屬性變為“loading”、相應的 readystatechange事件觸發時)的Unix毫秒時間戳。

PerformanceTiming.domInteractive 
返回當前網頁DOM結構結束解析、開始加載內嵌資源時(即Document.readyState屬性變為“interactive”、相應的readystatechange事件觸發時)的Unix毫秒時間戳。

PerformanceTiming.domContentLoadedEventStart 
返回當解析器發送DOMContentLoaded 事件,即所有需要被執行的腳本已經被解析時的Unix毫秒時間戳。

PerformanceTiming.domContentLoadedEventEnd 
返回當所有需要立即執行的腳本已經被執行(不論執行順序)時的Unix毫秒時間戳。

PerformanceTiming.domComplete 
返回當前文檔解析完成,即Document.readyState 變為 'complete'且相對應的readystatechange 被觸發時的Unix毫秒時間戳。

PerformanceTiming.loadEventStart 
返回該文檔下,load事件被發送時的Unix毫秒時間戳。如果這個事件還未被發送,它的值將會是0。

PerformanceTiming.loadEventEnd 
返回當load事件結束,即加載事件完成時的Unix毫秒時間戳。如果這個事件還未被發送,或者尚未完成,它的值將會是0

 

如何獲取PerformanceTiming

我們可以直接使用JS語法即可獲取到

<script>
    var mytiming = window.performance.timing;
    console.log(mytiming)
</script>

 

利用timing分析網頁

DNS查詢耗時 :domainLookupEnd - domainLookupStart
TCP鏈接耗時 :connectEnd - connectStart
SSL安全連接耗時: connectEnd - secureConnectionStart
request請求耗時 :responseEnd - responseStart
解析dom樹耗時 : domComplete - domInteractive
首次渲染時間/白屏時間 :responseStart - navigationStart
domready時間:domContentLoadedEventEnd - navigationStart
onload時間(總下載時間) :loadEventEnd - navigationStart

 

2、利用window.performance.getEntriesByType("navigation")來監控網頁的性能

window.performance.getEntriesByType("navigation")和performance.timing實際上是大同小異的,

只是說這個window.performance.getEntriesByType("navigation")的時間更精確

 

如何獲取window.performance.getEntriesByType("navigation")

我們可以直接使用JS語法即可獲取到

<script> var mytiming = window.performance.getEntriesByType("navigation")[0];
    console.log(mytiming)
</script>

 

利用timing分析網頁

DNS查詢耗時 :domainLookupEnd - domainLookupStart
TCP鏈接耗時 :connectEnd - connectStart
SSL安全連接耗時: connectEnd - secureConnectionStart
request請求耗時 :responseEnd - responseStart
解析dom樹耗時 : domComplete - domInteractive
首次渲染時間/白屏時間 :responseStart - startTime
domready時間 :domContentLoadedEventEnd - startTime
onload時間(總下載時間) :duration

 

二、頁面資源監控

1、頁面資源時間介紹

資源加載時間是指:網頁打開的過程中,各個圖片、JS、CSS文件加載的時間。

資源加載的各個階段的時間戳,如重定向、DNS查詢、TCP連接建立。這些階段和他們的屬性名在圖中列出。

 或者這么看也行

應用開發者可以使用這些屬性值去計算某個階段的耗時長度,用來幫助診斷性能問題。

我們還可以使用Chrome瀏覽器的開發者模式查看:

 

Queueing: 請求被阻塞,放入等待隊列中等待。
一般以下幾個原因會被阻塞:

  1. 如果這個資源加載優先級比較低,比如圖片(html/css/js的優先級比圖片高),那么圖片請求就會被渲染引擎阻塞,等待優先級高的資源加載完成才從隊列中取出,等待發送。
  2. 我們知道瀏覽器對同一域名下對TCP連接的並發數有限制(防止資源被消耗殆盡),chrome這邊是6,那么如果同一域名下請求多於6的話,后面的請求就會被阻塞。
  3. 等待釋放TCP連接

Stalled: 等待發送所用的時間,原因同上。

DNS Lookup:DNS查詢時間

Initail connection:建立TCP連接所用的時間

SSL:建立SSL連接所用的時間

Request sent:發出請求的時間,通常不到一毫秒

TTFB:第一字節時間,即請求發出到接受到服務器第一個字節的時間,如果這個時間太長,一般有兩個原因:

  1. 網絡太差
  2. 服務器響應太慢

一般建議不要這個階段的時間不要超過200毫秒。

Content Download:資源下載時間,如果被阻塞,則這個時間會很長,或者資源過大也會導致下載時間過長。例如js執行時間過長,那么圖片加載下來的時間就會被拉到很長。

 

2、Resource Timing API

那么我們怎么獲取這個資源時間呢?直接使用JS代碼調用這些公共接口即可:
var resources = window.performance.getEntriesByType('resource');
console.log(resources);

startTime
在資源提取開始的時間。該值等於fetchStart。

duration
資源完成下載的總時間,它是responseEnd和startTime屬性之間的差異。

redirectStart
重定向的開始時間。

redirectEnd
緊接在收到最后一次重定向響應的最后一個字節后。

fetchStart
如果是應用緩存在實現請求,將采集 fetchStart 時間。

domainLookupStart
DNS請求開始時間

domainLookupEnd
DNS請求結束時間

connectStart
開始建立與服務器的連接時間(TCP握手時間)。

connectEnd
在瀏覽器完成與服務器的連接以檢索資源之后(TCP握手結束時間)。

secureConnectionStart
在瀏覽器啟動握手過程之前,以保護當前連接(如果正在使用 TLS 或 SSL 將在握手(確保連接安全)開始時開始)。

requestStart
瀏覽器開始從服務器請求資源之前(在對某個資源的請求被發送到服務器后立即采集。)。

responseStart
在瀏覽器收到服務器響應的第一個字節后(服務器初始響應請求的時間)。

responseEnd
在瀏覽器收到資源的最后一個字節之后或緊接在傳輸連接關閉之前,以先到者為准(請求結束並且數據完成檢索的時間)。

transferSize
表示獲取資源的大小(以八位字節為單位)的數字。 包括響應頭字段和響應payload body的大小。

encodedBodySize
在刪除任何應用的內容編碼之前,從payload body的提取(HTTP或高速緩存)接收的大小(以八位字節為單位)的數字。

decodedBodySize
在刪除任何應用的內容編碼之后,從消息正文( message body )的提取(HTTP或緩存)接收的大小(以八位字節為單位)的數字。

serverTiming
包含服務器時序度量( timing metrics )的PerformanceServerTiming 條目數組。

 

那么,我們可以根據這些時間屬性,對某些資源進行解析:

整個過程時間: responseEnd - startTime 或者 duration
查看DNS查詢時間: domainLookupEnd - domainLookupStart
查看TCP三次握手時間(HTTP): connectEnd - connectStart
SSL握手時間(HTTPS協議會有SSL握手):connectEnd - secureConnectionStart
TTFB(首包時間):responseStart - startTime
響應時間(剩余包時間): responseEnd - responseStart

 

附上官方例子

各階段資源時間

function calculate_load_times() {
  // Check performance support
  if (performance === undefined) {
    console.log("= Calculate Load Times: performance NOT supported");
    return;
  }

  // Get a list of "resource" performance entries
  var resources = performance.getEntriesByType("resource");
  if (resources === undefined || resources.length <= 0) {
    console.log("= Calculate Load Times: there are NO `resource` performance records");
    return;
  }

  console.log("= Calculate Load Times");
  for (var i=0; i < resources.length; i++) {
    console.log("== Resource[" + i + "] - " + resources[i].name);
    // Redirect time
    var t = resources[i].redirectEnd - resources[i].redirectStart;
    console.log("... Redirect time = " + t);

    // DNS time
    t = resources[i].domainLookupEnd - resources[i].domainLookupStart;
    console.log("... DNS lookup time = " + t);

    // TCP handshake time
    t = resources[i].connectEnd - resources[i].connectStart;
    console.log("... TCP time = " + t);

    // Secure connection time
    t = (resources[i].secureConnectionStart > 0) ? (resources[i].connectEnd - resources[i].secureConnectionStart) : "0";
    console.log("... Secure connection time = " + t);

    // Response time
    t = resources[i].responseEnd - resources[i].responseStart;
    console.log("... Response time = " + t);

    // Fetch until response end
    t = (resources[i].fetchStart > 0) ? (resources[i].responseEnd - resources[i].fetchStart) : "0";
    console.log("... Fetch until response end time = " + t);

    // Request start until reponse end
    t = (resources[i].requestStart > 0) ? (resources[i].responseEnd - resources[i].requestStart) : "0";
    console.log("... Request start until response end time = " + t);

    // Start until reponse end
    t = (resources[i].startTime > 0) ? (resources[i].responseEnd - resources[i].startTime) : "0";
    console.log("... Start until response end time = " + t);
  }
}

 

資源大小

/*
應用程序資源的大小可能會影響應用程序的性能,因此獲取有關資源大小的准確數據非常重要(尤其是對於非托管資源)。該PerformanceResourceTiming接口具有三個屬性,可用於獲取有關資源的大小數據。該transferSize屬性返回獲取的資源的大小(以八位字節為單位),包括響應頭字段和響應有效載荷主體。該encodedBodySize屬性返回從有效內容主體的提取(HTTP或緩存)接收的大小(以八位字節為單位),然后再刪除任何應用的內容編碼。decodedBodySize從抓取(HTTP或高速緩存)的接收到的返回的大小(以字節)消息主體,之后除去任何施加的內容編碼。
*/
function display_size_data(){
  // Check for support of the PerformanceResourceTiming.*size properties and print their values
  // if supported.
  if (performance === undefined) {
    console.log("= Display Size Data: performance NOT supported");
    return;
  }

  var list = performance.getEntriesByType("resource");
  if (list === undefined) {
    console.log("= Display Size Data: performance.getEntriesByType() is  NOT supported");
    return;
  }

  // For each "resource", display its *Size property values
  console.log("= Display Size Data");
  for (var i=0; i < list.length; i++) {
    console.log("== Resource[" + i + "] - " + list[i].name);
    if ("decodedBodySize" in list[i])
      console.log("... decodedBodySize[" + i + "] = " + list[i].decodedBodySize);
    else
      console.log("... decodedBodySize[" + i + "] = NOT supported");

    if ("encodedBodySize" in list[i])
      console.log("... encodedBodySize[" + i + "] = " + list[i].encodedBodySize);
    else
      console.log("... encodedBodySize[" + i + "] = NOT supported");

    if ("transferSize" in list[i])
      console.log("... transferSize[" + i + "] = " + list[i].transferSize);
    else
      console.log("... transferSize[" + i + "] = NOT supported");
  }
}

 

在W3C Web性能工作組給我們帶來的 導航計時 在2012年,它是現在市面上幾乎所有的主流瀏覽器。導航計時定義了一個JavaScript API,用於測量主頁的性能。例如:

//導航時間
var t = performance.timing,
    pageloadtime = t.loadEventStart-t.navigationStart,
    dns = t.domainLookupEnd-t.domainLookupStart,
    tcp = t.connectEnd-t.connectStart,
    ttfb = t.responseStart-t.navigationStart;
擁有主頁的計時指標很棒,但是要診斷現實世界的性能問題,通常有必要深入研究各個資源。因此,我們擁有更新的 Resource Timing規范。該JavaScript API提供與導航計時類似的計時信息,但為每個單獨的資源提供。一個例子是:

//資源計時
var r0 = performance.getEntriesByType("resource")[0]
    加載時間= r0.duration,
    dns = r0.domainLookupEnd-r0.domainLookupStart,
    tcp = r0.connectEnd-r0.connectStart,
    ttfb = r0.responseStart-r0.startTime;

 

 
 
 
 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM