Performance — 前端性能監控利器
最近在寫一個監控腳本,終於有機會接觸到了這一塊,整理后寫下了本文。
Performance是一個做前端性能監控離不開的API,最好在頁面完全加載完成之后再使用,因為很多值必須在頁面完全加載之后才能得到。最簡單的辦法是在window.onload事件中讀取各種數據。
屬性
timing (PerformanceTiming)
從輸入url到用戶可以使用頁面的全過程時間統計,會返回一個PerformanceTiming對象,單位均為毫秒
按觸發順序排列所有屬性:(更詳細標准的解釋請參看:W3C Editor's Draft)navigationStart
:在同一個瀏覽器上下文中,前一個網頁(與當前頁面不一定同域)unload 的時間戳,如果無前一個網頁 unload ,則與 fetchStart 值相等unloadEventStart
:前一個網頁(與當前頁面同域)unload 的時間戳,如果無前一個網頁 unload 或者前一個網頁與當前頁面不同域,則值為 0unloadEventEnd
:和 unloadEventStart 相對應,返回前一個網頁 unload 事件綁定的回調函數執行完畢的時間戳redirectStart
:第一個 HTTP 重定向發生時的時間。有跳轉且是同域名內的重定向才算,否則值為 0redirectEnd
:最后一個 HTTP 重定向完成時的時間。有跳轉且是同域名內的重定向才算,否則值為 0fetchStart
:瀏覽器准備好使用 HTTP 請求抓取文檔的時間,這發生在檢查本地緩存之前domainLookupStart
:DNS 域名查詢開始的時間,如果使用了本地緩存(即無 DNS 查詢)或持久連接,則與 fetchStart 值相等domainLookupEnd
:DNS 域名查詢完成的時間,如果使用了本地緩存(即無 DNS 查詢)或持久連接,則與 fetchStart 值相等connectStart
:HTTP(TCP) 開始建立連接的時間,如果是持久連接,則與 fetchStart 值相等,如果在傳輸層發生了錯誤且重新建立連接,則這里顯示的是新建立的連接開始的時間connectEnd
:HTTP(TCP) 完成建立連接的時間(完成握手),如果是持久連接,則與 fetchStart 值相等,如果在傳輸層發生了錯誤且重新建立連接,則這里顯示的是新建立的連接完成的時間
注意:這里握手結束,包括安全連接建立完成、SOCKS 授權通過
secureConnectionStart
:HTTPS 連接開始的時間,如果不是安全連接,則值為 0requestStart
:HTTP 請求讀取真實文檔開始的時間(完成建立連接),包括從本地讀取緩存,連接錯誤重連時,這里顯示的也是新建立連接的時間responseStart
:HTTP 開始接收響應的時間(獲取到第一個字節),包括從本地讀取緩存responseEnd
:HTTP 響應全部接收完成的時間(獲取到最后一個字節),包括從本地讀取緩存domLoading
:開始解析渲染 DOM 樹的時間,此時 Document.readyState 變為 loading,並將拋出 readystatechange 相關事件domInteractive
:完成解析 DOM 樹的時間,Document.readyState 變為 interactive,並將拋出 readystatechange 相關事件
注意:只是 DOM 樹解析完成,這時候並沒有開始加載網頁內的資源
domContentLoadedEventStart
:DOM 解析完成后,網頁內資源加載開始的時間,文檔發生 DOMContentLoaded事件的時間domContentLoadedEventEnd
:DOM 解析完成后,網頁內資源加載完成的時間(如 JS 腳本加載執行完畢),文檔的DOMContentLoaded 事件的結束時間domComplete
:DOM 樹解析完成,且資源也准備就緒的時間,Document.readyState 變為 complete,並將拋出 readystatechange 相關事件loadEventStart
:load 事件發送給文檔,也即 load 回調函數開始執行的時間,如果沒有綁定 load 事件,值為 0loadEventEnd
:load 事件的回調函數執行完畢的時間,如果沒有綁定 load 事件,值為 0
常用計算:
DNS查詢耗時 :domainLookupEnd - domainLookupStart
TCP鏈接耗時 :connectEnd - connectStart
request請求耗時 :responseEnd - responseStart
解析dom樹耗時 : domComplete - domInteractive
白屏時間 :responseStart - navigationStart
domready時間(用戶可操作時間節點) :domContentLoadedEventEnd - navigationStart
onload時間(總下載時間) :loadEventEnd - navigationStart
navigation
旨在告訴開發者當前頁面是通過什么方式導航過來的,只有兩個屬性:type
,redirectCount
type:標志頁面導航類型,值如下表
type常數 | 枚舉值 | 描述 |
---|---|---|
TYPE_NAVIGATE | 0 | 普通進入,包括:點擊鏈接、在地址欄中輸入 URL、表單提交、或者通過除下表中 TYPE_RELOAD 和 TYPE_BACK_FORWARD 的方式初始化腳本。 |
TYPE_RELOAD | 1 | 通過刷新進入,包括:瀏覽器的刷新按鈕、快捷鍵刷新、location.reload()等方法。 |
TYPE_BACK_FORWARD | 2 | 通過操作歷史記錄進入,包括:瀏覽器的前進后退按鈕、快捷鍵操作、history.forward()、history.back()、history.go(num)。 |
TYPE_UNDEFINED | 255 | 其他非以上類型的方式進入。 |
注意:稍帶個小知識,history.go(url)這種非標准寫法目前主流瀏覽器均不支持,問題可參考http://stackoverflow.com/questions/6277283/history-gourl-issue
redirectCount:表示到達最終頁面前,重定向的次數,但是這個接口有同源策略限制,即僅能檢測同源的重定向。
注意:所有前端模擬的重定向都無法統計到,因為不屬於HTTP重定向
memory
描述內存多少,是在Chrome中添加的一個非標准屬性。jsHeapSizeLimit
: 內存大小限制totalJSHeapSize
: 可使用的內存usedJSHeapSize
: JS對象(包括V8引擎內部對象)占用的內存,不能大於totalJSHeapSize,如果大於,有可能出現了內存泄漏
方法
getEntries()
獲取所有資源請求的時間數據,這個函數返回一個按startTime排序的對象數組,數組成員除了會自動根據所請求資源的變化而改變以外,還可以用mark(),measure()方法自定義添加,該對象的屬性中除了包含資源加載時間還有以下五個屬性。name
:資源名稱,是資源的絕對路徑或調用mark方法自定義的名稱startTime
:開始時間duration
:加載時間entryType
:資源類型,entryType類型不同數組中的對象結構也不同!具體見下initiatorType
:誰發起的請求,具體見下
entryType的值:
值 | 該類型對象 | 描述 |
---|---|---|
mark | PerformanceMark | 通過mark()方法添加到數組中的對象 |
measure | PerformanceMeasure | 通過measure()方法添加到數組中的對象 |
resource | PerformanceResourceTiming | 所有資源加載時間,用處最多 |
navigation | PerformanceNavigationTiming | 現除chrome和Opera外均不支持,導航相關信息 |
frame | PerformanceFrameTiming | 現瀏覽器均未支持 |
server | PerformanceServerTiming | 未查到相關資料 |
initiatorType的值:
發起對象 | 值 | 描述 |
---|---|---|
a Element | link /script /img /iframe 等 |
通過標簽形式加載的資源,值是該節點名的小寫形式 |
a CSS resourc | css |
通過css樣式加載的資源,比如background的url方式加載資源 |
a XMLHttpRequest object | xmlhttprequest |
通過xhr加載的資源 |
a PerformanceNavigationTiming object | navigation |
當對象是PerformanceNavigationTiming時返回 |
//根據entryType類型返回的不同對象 PerformanceMark:{ //通過mark()方法添加的對象 entryType:"mark" name:調用mark()方法時自定義的名字 startTime: 做標記的時間 duration:0 } PerformanceMeasure:{ //通過measure()方法添加的對象 entryType:"measure" name:調用measure()方法時自定義的名字 startTime: 開始量的時間 duration:標記的兩個量的時間間隔 } PerformanceResourceTiming:{ //可以用來做一個精准的進度條 entryType:"resource" name:資源的絕對路徑,即URL startTime: 即將抓取資源的時間, duration: responseEnd - startTime initiatorType:略!/:傲嬌臉 //其他屬性請參考performance.timing } PerformanceNavigationTiming:{ entryType:"navigation" name:本頁路由,即地址欄看到的地址 startTime: 0 duration: loadEventEnd - startTime initiatorType:"navigation" //其他屬性請參考performance.timing }
請注意:
- 目前通過
<audio>
,<video>
加載資源,initiatorType還無法返回"audio"和"video",chrome中只能返回空字符串,firfox返回"other" - 如果一個圖片在頁面內既用img引入,又作為背景圖片引入,那么initiatorType返回的"img"
- performance.getEntries(params)這種形式仍出於草案階段,目前所有瀏覽器均為支持。但是非常有用,期待早些實現。
- 使用該方法統計資源信息的時候首先可以合理利用clearResourceTimings清除已統計過的對象避免重復統計,其次要過濾掉因上報統計數據而產生的對象。
getEntriesByName(name,type[optional]),getEntriesByType(type)
name
:想要篩選出的資源名type
:entryType的值中一個
返回值仍是一個數組,這個數組相當於getEntries()方法經過所填參數篩選后的一個子集
clearResourceTimings();
該方法無參數無返回值,可以清楚目前所有entryType為"resource"的數據,用於寫單頁應用的統計腳本非常有用
mark(name),measure(name, startMark, endMark),clearMarks(),clearMeasures()
用於做標記和清除標記,供用戶自定義統計一些數據,比如某函數運行耗時等
name
:自定義的名稱,不要和getEntries()返回的數組中其他name重復startMark
:作為開始時間的標記名稱或PerformanceTiming的一個屬性endMark
:作為結束時間的標記名稱或PerformanceTiming的一個屬性
創建標記:mark(name);
記錄兩個標記的時間間隔:measure(name, startMark, endMark);
清除指定標記:window.performance.clearMarks(name);
清除所有標記:window.performance.clearMarks();
清除指定記錄間隔數據:window.performance.clearMeasures(name);
清除所有記錄間隔數據:window.performance.clearMeasures();
now()
performance.now()是當前時間與performance.timing.navigationStart的時間差,以微秒(百萬分之一秒)為單位的時間,與 Date.now()-performance.timing.navigationStart的區別是不受系統程序執行阻塞的影響,因此更加精准。
友情提示
目前主流瀏覽器雖然都已支持Performance對象,但是並不能支持它上面的全部屬性和方法,本文主要依據chrome編寫,因此提到的chrome瀏覽器都是兼容的,其他具體使用時兼容性請自行測試,目前已測如下:
1.safari瀏覽器(包括mac和ios)只支持navigation,timing,now其余均不支持
2.微信瀏覽器支持timing,navigation屬性,不支持performance.getEntries方法
推薦及參考文章
MDN-Performance
W3C Editor's Draft
初探performance-監控網頁與程序性能
使用性能API快速分析web前端性能