最近有幸參與一個前端質量監控類項目的重構,算是我的初次接觸到前端質量監控相關的知識,對於其中的性能統計部分很感興趣,查詢資料以後寫了文章,做爲我的學習記錄,若有錯誤,敬請斧正css
經過瀏覽器提供的 window.performance.timing
方法,咱們可以獲得網頁每一個處理階段的精確時間。打開一個頁面後,這些信息會被瀏覽器記錄下來,咱們直接在控制檯輸出,就能夠查看結果html
該對象下有多個字段,每一個字段都對應着瀏覽器加載一個頁面的某個階段,下面這張圖詳細的展現了瀏覽器加載一個頁面的詳細過程前端
https://segmentfault.com/img/...html5
window.performance = { timing: { // 同一個域下,前一個網 unload 的時間戳, // 若是直接複製地址進入或者不是同域 // 則與 fetchStart 值相等 navigationStart: 1441112691935, // 前一個網頁unload 的時間戳, // 若是無前一個網頁 unload // 或者前一個網頁與當前頁面不一樣域,則值爲 0 unloadEventStart: 0, // 和 unloadEventStart 相對應 // 返回前一個網頁 unload 事件綁定的回調函數執行完畢的時間戳 unloadEventEnd: 0, // 第一個 HTTP 重定向發生時的時間。 // 有跳轉且是同域名內的重定向纔算 // 不然值爲 0 redirectStart: 0, // 最後一個 HTTP 重定向完成時的時間 // 有跳轉且是同域名內部的重定向纔算 // 不然值爲 0 redirectEnd: 0, // 瀏覽器準備好使用 HTTP 請求抓取文檔的時間 // 發生在檢查本地緩存以前 fetchStart: 1441112692155, // DNS 域名查詢開始的時間 // 若是使用了本地緩存(即無 DNS 查詢)或持久鏈接 // 則與 fetchStart 值相等 domainLookupStart: 1441112692155, // DNS 域名查詢完成的時間 // 若是使用了本地緩存(即無 DNS 查詢)或持久鏈接 // 則與 fetchStart 值相等 domainLookupEnd: 1441112692155, // HTTP(TCP) 開始創建鏈接的時間 // 若是是持久鏈接,則與 fetchStart 值相等 // 注意若是在傳輸層發生了錯誤且從新創建鏈接, // 則顯示的是新創建的鏈接開始的時間 connectStart: 1441112692155, // HTTP(TCP) 完成創建鏈接的時間(完成握手) // 若是是持久鏈接,則與 fetchStart 值相等 // 注意若是在傳輸層發生了錯誤且從新創建鏈接, // 則顯示的是新創建的鏈接完成的時間 // 注意這裏握手結束,包括安全鏈接創建完成、SOCKS 受權經過 connectEnd: 1441112692155, // HTTPS 鏈接開始的時間, // 若是不是安全鏈接,則值爲 0 secureConnectionStart: 0, // HTTP 請求讀取真實文檔開始的時間(完成創建鏈接 // 包括從本地讀取緩存 // 鏈接錯誤重連時,這裏顯示的也是新創建鏈接的時間 requestStart: 1441112692158, // HTTP 開始接收響應的時間(獲取到第一個字節) // 包括從本地讀取緩存 responseStart: 1441112692686, // HTTP 響應所有接收完成的時間(獲取到最後一個字節) // 包括從本地讀取緩存 responseEnd: 1441112692687, // 開始解析渲染 DOM 樹的時間 // 此時 Document.readyState 變爲 loading // 並將拋出 readystatechange 相關事件 domLoading: 1441112692690, // 完成解析 DOM 樹的時間 // Document.readyState 變爲 interactive // 並將拋出 readystatechange 相關事件 // 注意只是 DOM 樹解析完成 // 此時並無開始加載網頁內的資源 domInteractive: 1441112693093, // DOM 解析完成後,網頁內資源加載開始的時間 // 在 DOMContentLoaded 事件拋出前發生 domContentLoadedEventStart: 1441112693093, // DOM 解析完成後, // 網頁內資源加載完成的時間 // 如 JS 腳本加載執行完 domContentLoadedEventEnd: 1441112693101, // DOM 樹解析完成 // 且資源也準備就緒的時間 // Document.readyState 變爲 complete // 此時拋出 readystatechange 相關事件 domComplete: 1441112693214, // load 事件發送給文檔 // 也即 load 回調函數開始執行的時間 // 注意若是沒有綁定 load 事件,值爲 0 loadEventStart: 1441112693214, // load 事件的回調函數執行完畢的時間 loadEventEnd: 1441112693215 } }
經過這些值,咱們就能獲得某個階段具體的時間差,進行一些簡單的計算, 就可以獲得網頁的各項性能數據,便於咱們在某個階段作優化jquery
// DOM解析時間 var domParseTime = domComplete - responseEnd; // DNS解析時間 var domainLookUpTime = domainLookupEnd - domainLookupStart;
上面咱們用 window.performance.timing
方法獲得的是整個頁面的耗時,在一些狀況下,咱們想要知道頁面上某個靜態資源的加載時間,那麼咱們就能夠用三個方法來獲取這些信息ajax
window.performance.getEntries
// 返回網頁中全部資源和標記的數據window.performance.getEntriesByType
// 根據entryType返回數據window.performance.getEntriesByName
// 根據name返回數據其中 window.performance.getEntries
能夠獲取到全部資源信息和全部標記信息(何爲標記信息,咱們後面會講到), 而咱們想看的只是 entryType: "resource"
這些資源的信息,因此咱們能夠直接使用 window.performance.getEntriesByType('resource')
方法來得到咱們的信息,該方法會返回一個數組,包含字段以下bootstrap
var entries = [{ // 資源的絕對路徑 name: "http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js", // 資源類型 entryType: "resource", // css: 經過css請求的資源。類型不定 // img: 圖片 // link: 經過link標籤請求的資源: css/favicon // script: js文件 // xmlhttprequest: 通常爲GET方式的數據接口 initiatorType: "script", // 加載時間 duration: 18.13399999809917, // 這些字段的意義同上面 // 不一樣之處只表示這一個資源的時間 redirectStart: 0, redirectEnd: 0, fetchStart: 233.346999992829828, domainLookupStart: 0, domainLookupEnd: 0, connectStart: 0, connectEnd: 0, secureConnectionStart: 0, requestStart: 0, responseStart: 0, responseEnd: 442.7109999960521, startTime: 424.57699999795295 }];
這些數據裏面,initiatorType
的值須要注意下, initiatorType
並非指資源的類型,而是指 請求該資源的請求類型, 如上圖所示 initiatorType: 'css'
的時候,資源文件並非一個css文件,而是一張 img圖片,代表該圖片是經過css去請求到的(一般是設置背景圖),而直接在網頁中經過 <img />
或在代碼中經過 new Image
請求的圖片,initiatorType
值都爲 img
,咱們如下面這段代碼爲例segmentfault
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <link rel="stylesheet" href="index.css"> <link rel="stylesheet" href="http://apps.bdimg.com/libs/bootstrap/3.3.4/css/bootstrap.css"> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> <style> body > div { height: 300px; width: 300px; margin: 10px; float: left; } .css-img div { background: url('http://temp.im/110x110/FF2D55/000') no-repeat; } </style> </head> <body> <div class="css-img"> <p>經過CSS加載的圖片</p> <div></div> </div> <div class="normal-img"> <p>經過 img 標籤直接加載</p> <img src="http://temp.im/178x178/007AFF/fff" /> </div> <div class="new-img"> <p>經過 new Image() 加載的圖片</p> </div> </body> <script> window.onload = function () { var tempImage = new Image(); tempImage.src = "http://temp.im/288x288/FF9500/000"; document.querySelector('.new-img').appendChild(tempImage) } </script> </html>
打開頁面以後在控制檯輸出結果, 也驗證告終果api
另外值得注意的一個點是,除了這三張圖片,咱們在頁面中還加載了2個css文件和1個js文件,分別是數組
仔細觀察的話,會發現這幾個文件有一些不一樣之處,咱們對數據稍做處理,方便參看
咱們會發現,其中的 bootstrap.min.css
的不少字段數據都爲0,查詢資料以後才知道,該方法一般狀況下只能得到本域名下資源的加載信息(因此 index.css
的信息直接就能獲取到)
若是想要獲取遠程資源的加載信息的只有在response裏面顯式的設置 Timing-Allow-Origin:*
相似於(Acces-Control-Allow-Origin的設置)
咱們直接打開了 jquery.min.js
鏈接, 看到了 Timing-Allow-Origin
字段,因此咱們可以得到該資源的準確信息, 而在 bootstrap.min.css
的返回中,咱們沒有找到 Timing-Allow-Origin
字段, 因此數據都顯示爲0
一般狀況下,咱們想要取得一段代碼的執行時間,會作如下操做
var start = +new Date(); for (var i = 0; i < 100; i++) { ret.push(i); } var end = +new Date(); console.log('執行時間', end - start);
但不足之處在於, new Date
的精確度只到秒, 而有時候咱們的代碼執行在1s以內,計算出的差值爲0,此時就顯得無能爲力。因此,咱們可能經過以下兩種方式,解決這個問題
window.performance.now
var start = window.performance.now(); for (var i = 0; i < 100; i++) { ret.push(i); } var end = window.performance.now(); console.log('執行時間', end - start);
使用方式十分簡單,該方法可以計算出精確到 百萬分之一秒 的時間差。與 new Date
不一樣之處在於 window.performance.now
方法返回的是相對於 performance.timing.navigationStart
即頁面初始化的時間,但咱們的關注點是差值, 因此時間從哪兒算起,對於咱們來講並不重要
window.performance.mark
和 window.performance.measure
// 標記開始 window.performance.mark("start"); for (var i = 0; i < 100; i++) { ret.push(i); } // 標記結束 window.performance.mark("end"); // 計算差值並命名爲difference, 無返回 window.performance.measure("difference", "start", "end");
經過 window.performance.mark
方法,咱們在指定地方打上一個記號,此時不會返回任何值,會被記錄到performance
對象裏, entryType
被默認標記爲了 mark
。
而後經過 window.performance.measure
計算出差值,並經過第一個參數對其賦名爲 difference
, 此時也沒有返回值,這些值也被記錄到了 performance
對象裏,它們的 entryType
被默認標記爲了 measure
.
因此咱們能夠經過兩種方式來查看差值
window.performance.getEntriesByType('measure')
esByName('difference')`
![圖片上傳中...]
在使用完了以後,咱們還能夠對這些標識,進行統一的清理
// 清除指定標記點 window.performance.clearMarks('start'); // 清除全部標記點 window.performance.clearMarks(); // 清除指定差值數據 window.performance.clearMeasures('difference'); // 清除全部差值數據 window.performance.clearMeasures();
經過這兩種方式,均可以精確的計算出十分精確的時間差值,那如何作選擇呢? 個人建議是,若是隻是單純的想要獲得少數的差值,直接在代碼中使用 now
, 而若是須要統計大量的值, 就應該使用 mark
和 measure
方法,由於這些值都會存在 performance
對象裏,咱們能夠在任意須要的時候,經過讀取數組,十分便捷的對數據作統一處理與管理