如果我們想要對一個網頁進行性能監控,那么使用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: 請求被阻塞,放入等待隊列中等待。
一般以下幾個原因會被阻塞:
- 如果這個資源加載優先級比較低,比如圖片(html/css/js的優先級比圖片高),那么圖片請求就會被渲染引擎阻塞,等待優先級高的資源加載完成才從隊列中取出,等待發送。
- 我們知道瀏覽器對同一域名下對TCP連接的並發數有限制(防止資源被消耗殆盡),chrome這邊是6,那么如果同一域名下請求多於6的話,后面的請求就會被阻塞。
- 等待釋放TCP連接
Stalled: 等待發送所用的時間,原因同上。
DNS Lookup:DNS查詢時間
Initail connection:建立TCP連接所用的時間
SSL:建立SSL連接所用的時間
Request sent:發出請求的時間,通常不到一毫秒
TTFB:第一字節時間,即請求發出到接受到服務器第一個字節的時間,如果這個時間太長,一般有兩個原因:
- 網絡太差
- 服務器響應太慢
一般建議不要這個階段的時間不要超過200毫秒。
Content Download:資源下載時間,如果被阻塞,則這個時間會很長,或者資源過大也會導致下載時間過長。例如js執行時間過長,那么圖片加載下來的時間就會被拉到很長。
2、Resource Timing API
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;