Performance — 前端性能監控利器

Performance是一個作前端性能監控離不開的API,最好在頁面徹底加載完成以後再使用,由於不少值必須在頁面徹底加載以後才能獲得。最簡單的辦法是在window.onload事件中讀取各類數據。css

大概長這個樣子,小程序也支持前端

屬性

timing (PerformanceTiming)

從輸入url到用戶可使用頁面的全過程時間統計,會返回一個PerformanceTiming對象,單位均爲毫秒
ios

按觸發順序排列全部屬性:(更詳細標準的解釋請參看:W3C Editor's Draft)
navigationStart:在同一個瀏覽器上下文中,前一個網頁(與當前頁面不必定同域)unload 的時間戳,若是無前一個網頁 unload ,則與 fetchStart 值相等
unloadEventStart:前一個網頁(與當前頁面同域)unload 的時間戳,若是無前一個網頁 unload 或者前一個網頁與當前頁面不一樣域,則值爲 0
unloadEventEnd:和 unloadEventStart 相對應,返回前一個網頁 unload 事件綁定的回調函數執行完畢的時間戳
redirectStart:第一個 HTTP 重定向發生時的時間。有跳轉且是同域名內的重定向纔算,不然值爲 0
redirectEnd:最後一個 HTTP 重定向完成時的時間。有跳轉且是同域名內的重定向纔算,不然值爲 0
fetchStart:瀏覽器準備好使用 HTTP 請求抓取文檔的時間,這發生在檢查本地緩存以前
domainLookupStart:DNS 域名查詢開始的時間,若是使用了本地緩存(即無 DNS 查詢)或持久鏈接,則與 fetchStart 值相等
domainLookupEnd:DNS 域名查詢完成的時間,若是使用了本地緩存(即無 DNS 查詢)或持久鏈接,則與 fetchStart 值相等
connectStart:HTTP(TCP) 開始創建鏈接的時間,若是是持久鏈接,則與 fetchStart 值相等,若是在傳輸層發生了錯誤且從新創建鏈接,則這裏顯示的是新創建的鏈接開始的時間
connectEnd:HTTP(TCP) 完成創建鏈接的時間(完成握手),若是是持久鏈接,則與 fetchStart 值相等,若是在傳輸層發生了錯誤且從新創建鏈接,則這裏顯示的是新創建的鏈接完成的時間git

注意:這裏握手結束,包括安全鏈接創建完成、SOCKS 受權經過github

secureConnectionStart:HTTPS 鏈接開始的時間,若是不是安全鏈接,則值爲 0
requestStart:HTTP 請求讀取真實文檔開始的時間(完成創建鏈接),包括從本地讀取緩存,鏈接錯誤重連時,這裏顯示的也是新創建鏈接的時間
responseStart:HTTP 開始接收響應的時間(獲取到第一個字節),包括從本地讀取緩存
responseEnd:HTTP 響應所有接收完成的時間(獲取到最後一個字節),包括從本地讀取緩存
domLoading:開始解析渲染 DOM 樹的時間,此時 Document.readyState 變爲 loading,並將拋出 readystatechange 相關事件
domInteractive:完成解析 DOM 樹的時間,Document.readyState 變爲 interactive,並將拋出 readystatechange 相關事件web

注意:只是 DOM 樹解析完成,這時候並無開始加載網頁內的資源chrome

domContentLoadedEventStart:DOM 解析完成後,網頁內資源加載開始的時間,文檔發生 DOMContentLoaded事件的時間
domContentLoadedEventEnd:DOM 解析完成後,網頁內資源加載完成的時間(如 JS 腳本加載執行完畢),文檔的DOMContentLoaded 事件的結束時間
domComplete:DOM 樹解析完成,且資源也準備就緒的時間,Document.readyState 變爲 complete,並將拋出 readystatechange 相關事件
loadEventStart:load 事件發送給文檔,也即 load 回調函數開始執行的時間,若是沒有綁定 load 事件,值爲 0
loadEventEnd:load 事件的回調函數執行完畢的時間,若是沒有綁定 load 事件,值爲 0小程序

// 計算加載時間
function  getPerformanceTiming () { 
     var  performance = window.performance;
  
     if  (!performance) {
         // 當前瀏覽器不支持
         console.log( '你的瀏覽器不支持 performance 接口' );
         return ;
     }
  
     var  t = performance.timing;
     var  times = {};
  
     //【重要】頁面加載完成的時間
     //【緣由】這幾乎表明了用戶等待頁面可用的時間
     times.loadPage = t.loadEventEnd - t.navigationStart;
  
     //【重要】解析 DOM 樹結構的時間
     //【緣由】檢討下你的 DOM 樹嵌套是否是太多了!
     times.domReady = t.domComplete - t.responseEnd;
  
     //【重要】重定向的時間
     //【緣由】拒絕重定向!好比,http://example.com/ 就不應寫成 http://example.com
     times.redirect = t.redirectEnd - t.redirectStart;
  
     //【重要】DNS 查詢時間
     //【緣由】DNS 預加載作了麼?頁面內是否是使用了太多不一樣的域名致使域名查詢的時間太長?
     // 可以使用 HTML5 Prefetch 預查詢 DNS ,見:[HTML5 prefetch](http://segmentfault.com/a/1190000000633364)           
     times.lookupDomain = t.domainLookupEnd - t.domainLookupStart;
  
     //【重要】讀取頁面第一個字節的時間
     //【緣由】這能夠理解爲用戶拿到你的資源佔用的時間,加異地機房了麼,加CDN 處理了麼?加帶寬了麼?加 CPU 運算速度了麼?
     // TTFB 即 Time To First Byte 的意思
     times.ttfb = t.responseStart - t.navigationStart;
 
 
  //【重要】內容加載完成的時間
     //【緣由】頁面內容通過 gzip 壓縮了麼,靜態資源 css/js 等壓縮了麼?
     times.request = t.responseEnd - t.requestStart;
  
     //【重要】執行 onload 回調函數的時間
     //【緣由】是否太多沒必要要的操做都放到 onload 回調函數裏執行了,考慮過延遲加載、按需加載的策略麼?
     times.loadEvent = t.loadEventEnd - t.loadEventStart;
  
     // DNS 緩存時間
     times.appcache = t.domainLookupStart - t.fetchStart;
  
     // 卸載頁面的時間
     times.unloadEvent = t.unloadEventEnd - t.unloadEventStart;
  
     // TCP 創建鏈接完成握手的時間
     times.connect = t.connectEnd - t.connectStart;
  
     return  times;
}

 


經常使用計算:
segmentfault

DNS查詢耗時 :domainLookupEnd - domainLookupStart
TCP連接耗時 :connectEnd - connectStart
request請求耗時 :responseEnd - responseStart
解析dom樹耗時 : domComplete - domInteractive
白屏時間 :responseStart - navigationStart
domready時間(用戶可操做時間節點) :domContentLoadedEventEnd - navigationStart
onload時間(總下載時間) :loadEventEnd - navigationStartapi

navigation

旨在告訴開發者當前頁面是經過什麼方式導航過來的,只有兩個屬性:typeredirectCount

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()

這個函數返回的將是一個數組,包含了頁面中全部的 HTTP 請求.注意 HTTP 請求有可能命中本地緩存,因此請求響應的間隔將很是短 能夠看到,與 performance.timing 對比: 沒有與 DOM 相關的屬性

獲取全部資源請求的時間數據,這個函數返回一個按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時返回
  1. //根據entryType類型返回的不一樣對象

  2. PerformanceMark:{ //經過mark()方法添加的對象

  3. entryType:"mark"

  4. name:調用mark()方法時自定義的名字

  5. startTime: 作標記的時間

  6. duration:0

  7. }

  8. PerformanceMeasure:{ //經過measure()方法添加的對象

  9. entryType:"measure"

  10. name:調用measure()方法時自定義的名字

  11. startTime: 開始量的時間

  12. duration:標記的兩個量的時間間隔

  13. }

  14. PerformanceResourceTiming:{ //能夠用來作一個精準的進度條

  15. entryType:"resource"

  16. name:資源的絕對路徑,即URL

  17. startTime: 即將抓取資源的時間,

  18. duration: responseEnd - startTime

  19. initiatorType:略!/:傲嬌臉

  20. //其餘屬性請參考performance.timing

  21. }

  22. PerformanceNavigationTiming:{

  23. entryType:"navigation"

  24. name:本頁路由,即地址欄看到的地址

  25. startTime: 0

  26. duration: loadEventEnd - startTime

  27. initiatorType:"navigation"

  28. //其餘屬性請參考performance.timing

  29. }

請注意:

  1. 目前經過<audio><video>加載資源,initiatorType還沒法返回"audio"和"video",chrome中只能返回空字符串,firfox返回"other"
  2. 若是一個圖片在頁面內既用img引入,又做爲背景圖片引入,那麼initiatorType返回的"img"
  3. performance.getEntries(params)這種形式仍出於草案階段,目前全部瀏覽器均爲支持。可是很是有用,期待早些實現。
  4. 使用該方法統計資源信息的時候首先能夠合理利用clearResourceTimings清除已統計過的對象避免重複統計,其次要過濾掉因上報統計數據而產生的對象。

 

能夠像 getPerformanceTiming 獲取網頁的時間同樣,獲取某個資源的時間:

// 計算加載時間
function  getEntryTiming (entry) { 
     var  t = entry;
     var  times = {};
  
     // 重定向的時間
     times.redirect = t.redirectEnd - t.redirectStart;
  
     // DNS 查詢時間
     times.lookupDomain = t.domainLookupEnd - t.domainLookupStart;
  
     // 內容加載完成的時間
     times.request = t.responseEnd - t.requestStart;
  
     // TCP 創建鏈接完成握手的時間
     times.connect = t.connectEnd - t.connectStart;
  
     // 掛載 entry 返回
     times.name = entry.name;
     times.entryType = entry.entryType;
     times.initiatorType = entry.initiatorType;
     times.duration = entry.duration;
  
     return  times;
}
  
// test
// var entries = window.performance.getEntries();
// entries.forEach(function (entry) {
//     var times = getEntryTiming(entry);
//     console.log(times);
// });

 

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();

 

使用 performance.mark() 也能夠精確計算程序執行時間

使用 performance.mark() 標記各類時間戳(就像在地圖上打點),保存爲各類測量值(測量地圖上的點之間的距離),即可以批量地分析這些數據了。

直接上示例代碼看註釋便明白:

function  randomFunc (n) { 
     if  (!n) {
         // 生成一個隨機數
         n = ~~(Math.random() * 10000);
     }
     var  nameStart =  'markStart'  + n;
     var  nameEnd   =  'markEnd'  + n;
     // 函數執行前作個標記
     window.performance.mark(nameStart);
  
     for  ( var  i = 0; i < n; i++) {
         // do nothing
     }
  
     // 函數執行後再作個標記
     window.performance.mark(nameEnd);
  
     // 而後測量這個兩個標記間的時間距離,並保存起來
     var  name =  'measureRandomFunc'  + n;
     window.performance.measure(name, nameStart, nameEnd);
}
  
// 執行三次看看
randomFunc(); 
randomFunc(); 
// 指定一個名字
randomFunc(888); 
// 看下保存起來的標記 mark
var  marks = window.performance.getEntriesByType( 'mark' ); 
console.log(marks);

// 看下保存起來的測量 measure
var  measure = window.performance.getEntriesByType( 'measure' ); 
console.log(measure);

// 看下咱們自定義的測量
var  entries = window.performance.getEntriesByName( 'measureRandomFunc888' ); 
console.log(entries);

 

能夠看到,for 循環 measureRandomFunc888 的時候

結束時間爲: 4875.1199999969685

開始時間爲:4875.112999987323

執行時間爲:4875.1199999969685 – 4875.112999987323 = 0.00700000964

標記和測量用完了能夠清除掉:

// 清除指定標記
window.performance.clearMarks( 'markStart888' ); 
// 清除全部標記
window.performance.clearMarks();
  
// 清除指定測量
window.performance.clearMeasures( 'measureRandomFunc' ); 
// 清除全部測量
window.performance.clearMeasures();

固然 performance.mark() 只是提供了一些簡便的測量方式,好比以前咱們測量 domReady 是這麼測的:

// 計算 domReady 時間
var  t = performance.timing 
var  domReadyTime = t.domComplete - t.responseEnd; 
console.log(domReadyTime)

其實就能夠寫成:

window.performance.measure( 'domReady' , 'responseEnd'  'domComplete' ); 
var  domReadyMeasure = window.performance.getEntriesByName( 'domReady' ); 
console.log(domReadyMeasure);

使用 performance.now() 精確計算程序執行時間

谷歌以毫秒爲單位,更精確。

performance.now() 與 Date.now() 不一樣的是,返回了以微秒(百萬分之一秒)爲單位的時間,更加精準。 ??????

而且與 Date.now() 會受系統程序執行阻塞的影響不一樣,performance.now() 的時間是以恆定速率遞增的,不受系統時間的影響(系統時間可被人爲或軟件調整)。

注意 Date.now() 輸出的是 UNIX 時間,即距離 1970 的時間,而 performance.now() 輸出的是相對於 performance.timing.navigationStart(頁面初始化) 的時間。

使用 Date.now() 的差值並不是絕對精確,由於計算時間時受系統限制(可能阻塞)。但使用 performance.now() 的差值,並不影響咱們計算程序執行的精確時間。

// 計算程序執行的精確時間
function  getFunctionTimeWithDate (func) { 
     var  timeStart = Data.now();
  
     // 執行開始
     func();
     // 執行結束
     var  timeEnd = Data.now();
  
     // 返回執行時間
     return  (timeEnd - timeStart);
}
function  getFunctionTimeWithPerformance (func) { 
     var  timeStart = window.performance.now();
  
     // 執行開始
     func();
     // 執行結束
     var  timeEnd = window.performance.now();
  
     // 返回執行時間
     return  (timeEnd - timeStart);
}

 

 

友情提示

目前主流瀏覽器雖然都已支持Performance對象,可是並不能支持它上面的所有屬性和方法,本文主要依據chrome編寫,所以提到的chrome瀏覽器都是兼容的,其餘具體使用時兼容性請自行測試,目前已測以下:
1.safari瀏覽器(包括mac和ios)只支持navigation,timing,now其他均不支持
2.微信瀏覽器支持timing,navigation屬性,不支持performance.getEntries方法

推薦及參考文章

MDN-Performance
W3C Editor's Draft
初探performance-監控網頁與程序性能
使用性能API快速分析web前端性能

相關文章
相關標籤/搜索