閱讀目錄javascript
一:什麼是Performance?css
Performance是前端性能監控的API。它能夠檢測頁面中的性能,W3C性能小組引入進來的一個新的API,它能夠檢測到白屏時間、首屏時間、用戶可操做的時間節點,頁面總下載的時間、DNS查詢的時間、TCP連接的時間等。所以咱們下面來學習下這個API。html
那麼在學習以前,前端性能最主要的測試點有以下幾個:前端
白屏時間:從咱們打開網站到有內容渲染出來的時間點。
首屏時間:首屏內容渲染完畢的時間節點。
用戶可操做時間節點:domready觸發節點。
總下載時間:window.onload的觸發節點。vue
咱們如今在html中來簡單的使用下performance的基本代碼:以下代碼所示:java
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>performance演示</title> </head> <body> <script type="text/javascript"> var performance = window.performance || window.msPerformance || window.webkitPerformance; if (performance) { console.log(performance); } </script> </body> </html>
而後在瀏覽器下會打印以下 performance基本信息以下:react
如上能夠看到,performance包含三個對象,分別爲 memory、navigation、timing. 其中 memory 是和內存相關的,navigation是指來源相關的,也就是說從那個地方跳轉過來的。timing是關鍵點時間。下面咱們來分別介紹下該對象有哪些具體的屬性值。jquery
performance.memory 含義是顯示此刻內存佔用的狀況,從如上圖能夠看到,該對象有三個屬性,分別爲:webpack
jsHeapSizeLimit 該屬性表明的含義是:內存大小的限制。
totalJSHeapSize 表示 總內存的大小。
usedJSHeapSize 表示可以使用的內存的大小。
若是 usedJSHeapSize 大於 totalJSHeapSize的話,那麼就會出現內存泄露的問題,所以是不容許大於該值的。git
performance.navigation 含義是頁面的來源信息,該對象有2個屬性值,分別是:redirectCount 和 type。
redirectCount: 該值的含義是:若是有重定向的話,頁面經過幾回重定向跳轉而來,默認爲0;
type:該值的含義表示的頁面打開的方式。默認爲0. 可取值爲0、一、二、255.
0(TYPE_NAVIGATE):表示正常進入該頁面(非刷新、非重定向)。
1(TYPE_RELOAD):表示經過 window.location.reload 刷新的頁面。若是我如今刷新下頁面後,再來看該值就變成1了。
2(TYPE_BACK_FORWARD ):表示經過瀏覽器的前進、後退按鈕進入的頁面。若是我此時先前進下頁面,再後退返回到該頁面後,查看打印的值,發現變成2了。
255(TYPE_RESERVED): 表示非以上的方式進入頁面的。
以下圖所示:
performance.onresourcetimingbufferfull; 如上截圖也有這個屬性的,該屬性的含義是在一個回調函數。該回調函數會在瀏覽器的資源時間性能緩衝區滿了的時候會執行的。
performance.timeOrigin:是一系列時間點的基準點,精確到萬分之一毫秒。如上截圖該值爲:1559526951495.139,該值是
一個動態的,刷新下,該值是會發生改變的。
performance.timing:是一系列關鍵時間點,它包含了網絡、解析等一系列的時間數據。
爲了方便,從網上弄了一張圖片過來,來解析下各個關鍵時間點的含義以下所示:
按照如上圖的順序,咱們來分別看下各個字段的含義以下:
navigationStart: 含義爲:同一個瀏覽器上一個頁面卸載結束時的時間戳。若是沒有上一個頁面的話,那麼該值會和fetchStart的值相同。
redirectStart: 該值的含義是第一個http重定向開始的時間戳,若是沒有重定向,或者重定向到一個不一樣源的話,那麼該值返回爲0.
redirectEnd: 最後一個HTTP重定向完成時的時間戳。若是沒有重定向,或者重定向到一個不一樣的源,該值也返回爲0.
fetchStart: 瀏覽器準備好使用http請求抓取文檔的時間(發生在檢查本地緩存以前)。
domainLookupStart: DNS域名查詢開始的時間,若是使用了本地緩存話,或 持久連接,該值則與fetchStart值相同。
domainLookupEnd: DNS域名查詢完成的時間,若是使用了本地緩存話,或 持久連接,該值則與fetchStart值相同。
connectStart: HTTP 開始創建鏈接的時間,若是是持久連接的話,該值則和fetchStart值相同,若是在傳輸層發生了錯誤且須要從新創建鏈接的話,那麼在這裏顯示的是新創建的連接開始時間。
secureConnectionStart: HTTPS 鏈接開始的時間,若是不是安全鏈接,則值爲 0
connectEnd:HTTP完成創建鏈接的時間(完成握手)。若是是持久連接的話,該值則和fetchStart值相同,若是在傳輸層發生了錯誤且須要從新創建鏈接的話,那麼在這裏顯示的是新創建的連接完成時間。
requestStart: http請求讀取真實文檔開始的時間,包括從本地讀取緩存,連接錯誤重連時。
responseStart: 開始接收到響應的時間(獲取到第一個字節的那個時候)。包括從本地讀取緩存。
responseEnd: HTTP響應所有接收完成時的時間(獲取到最後一個字節)。包括從本地讀取緩存。
unloadEventStart: 前一個網頁(和當前頁面同域)unload的時間戳,若是沒有前一個網頁或前一個網頁是不一樣的域的話,那麼該值爲0.
unloadEventEnd: 和 unloadEventStart 相對應,返回是前一個網頁unload事件綁定的回調函數執行完畢的時間戳。
domLoading: 開始解析渲染DOM樹的時間。
domInteractive: 完成解析DOM樹的時間(只是DOM樹解析完成,可是並無開始加載網頁的資源)。
domContentLoadedEventStart:DOM解析完成後,網頁內資源加載開始的時間。
domContentLoadedEventEnd: DOM解析完成後,網頁內資源加載完成的時間。
domComplete: DOM樹解析完成,且資源也準備就緒的時間。Document.readyState 變爲 complete,並將拋出 readystatechange 相關事件。
loadEventStart: load事件發送給文檔。也即load回調函數開始執行的時間,若是沒有綁定load事件,則該值爲0.
loadEventEnd: load事件的回調函數執行完畢的時間,若是沒有綁定load事件,該值爲0.
如上就是各個值的含義了,你們簡單的看下,瞭解下就好了,不用過多的折騰。在使用這些值來計算白屏時間、首屏時間、用戶可操做的時間節點,頁面總下載的時間、DNS查詢的時間、TCP連接的時間等以前,咱們能夠先看下傳統方案是如何作的?
傳統方案
在該API出現以前,咱們想要計算出如上前端性能的話,咱們須要使用時間戳來大概估計下要多長時間。好比使用:(new Date()).getTime() 來計算以前和以後的值,而後兩個值的差值就是這段的時間已用的時間。可是該方法有偏差,不許確。下面咱們來看看傳統的方案以下:
1.1 白屏時間
白屏時間:是指用戶進入該網站(好比刷新頁面、跳轉到新頁面等經過該方式)的時刻開始計算,一直到頁面內容顯示出來以前的時間節點。如上圖咱們能夠看到,這個過程包括dns查詢、創建tcp連接、發送首個http請求等過程、返回html文檔。
好比以下代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>performance演示</title> <script type="text/javascript"> var startTime = (new Date()).getTime(); </script> <link href="xx1.css" rel="stylesheet" /> <link href="xx2.css" rel="stylesheet" /> <script type="text/javascript" src="xx1.js"></script> <script type="text/javascript" src="xx2.js"></script> <script type="text/javascript"> var endTime = (new Date()).getTime(); </script> </head> <body> <script type="text/javascript"> </script> </body> </html>
如上代碼,endTime - startTime 的值就能夠看成爲白屏時間的估值了。
1.2 首屏時間
要獲取首屏時間的計算,首先咱們要知道頁面加載有2種方式:
1. 加載完資源文件後經過js動態獲取接口數據,而後數據返回回來渲染內容。
所以會有以下所示的信息圖:以下所示:
2. 同構直出前端頁面,以下所示:
css資源文件加載時間的計算,咱們能夠如上圖所示:t2-t1 就是全部的css加載的時間。
所以假如咱們如今的項目文件代碼index.html 代碼以下所示:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>performance演示</title> <script type="text/javascript"> // 獲取頁面開始的時間 var pageStartTime = (new Date()).getTime(); </script> <link href="xx1.css" rel="stylesheet" /> <link href="xx2.css" rel="stylesheet" /> <script type="text/javascript"> // 獲取加載完成後的css的時間 var cssEndTime = (new Date()).getTime(); </script> <script type="text/javascript" src='./jquery.js'></script> <script type="text/javascript"> // 獲取加載完 jquery插件的時間 var jsPluginTime = (new Date()).getTime(); </script> </head> <body> <h1>計算時間</h1> <script type="text/javascript"> // 加載js的開始時間 var JsStartTime = (new Date()).getTime(); </script> <script type="text/javascript" src="xx1.js"></script> <script type="text/javascript" src="xx2.js"></script> <script type="text/javascript"> // 加載完成後的js的結束時間 var JsEndTime = (new Date()).getTime(); </script> </body> </html>
如上代碼,能夠獲取頁面開始的時間 pageStartTime, 加載資源文件css後的時間就是 cssEndTime - pageStartTime,加載jquery插件的時間 jsPluginTime - cssEndTime 了。可是 js的加載時間難道就是 = JsEndTime - JsStartTime 嗎?這確定不是的,由於JS還須要有執行的時間的。好比js內部作了不少dom操做,那麼dom操做須要時間的,那麼js加載和執行的時間 = JsEndTime - JsStartTime; 嗎?這也是不對的,由於瀏覽器加載資源文件是並行的,執行js文件是串行的。那若是css文件或jquery文件發起http請求後一直沒有返回,那麼它會阻塞後續js文件的執行的。可是此時此刻js文件加載很早就已經返回了,可是因爲服務器緣由或網絡緣由致使css文件加載很慢,因此會堵塞js文件的執行。
所以咱們能夠總結爲:js加載的時間 不等於 JsEndTime - JsStartTime;同理js加載和執行的實際 也不等於 JsEndTime - JsStartTime。正由於有外鏈css中的http請求,它會堵塞js的執行,所以不少網站會把外鏈css文件改爲內聯css文件代碼,內聯css代碼是串行的。好比百度,淘寶官網等。下面咱們來看下這兩個網站的源碼:
百度源碼:
咱們打開百度搜索頁面,而後咱們右鍵查看網頁的源碼以下:
咱們再來看下百度搜索頁面的網絡css的請求能夠看到以下,同域名下根本就沒有css外鏈操做,全部的css代碼都是內聯的,咱們查看網絡看到就僅僅有一個css外鏈請求,而且該css並非百度內部的css文件,以下所示:
淘寶官網源碼:
咱們操做如上所示,打開淘寶官網源碼查看以下所示:
而且咱們查看淘寶官網的網絡請求中只看到兩個css外鏈請求,且該兩個外鏈請求並非淘寶內部的同域名下的css請求,
以下所示:
而且 該css文件名爲 new_suggest-min.css 文件,我經過源碼搜索下,並無發現該css文件外鏈,所以能夠確定的是該css文件是經過js動態加載進去的,以下所示:
切記:若是body下面有多個js文件的話,而且有ajax動態渲染的文件的話,那麼儘可能讓他放在最前面,由於其餘的js加載的時候會阻止頁面的渲染,致使渲染js一直渲染不了,致使頁面的數據會有一段時間是空白的狀況。
二:使用 performance.timing 來計算值
performance 對象有一個timing屬性,該屬性包含不少屬性值,咱們仍是來看看以前的示意圖,以下所示:
從上面的示意圖咱們能夠看到:
重定向耗時 = redirectEnd - redirectStart; DNS查詢耗時 = domainLookupEnd - domainLookupStart; TCP連接耗時 = connectEnd - connectStart; HTTP請求耗時 = responseEnd - responseStart; 解析dom樹耗時 = domComplete - domInteractive; 白屏時間 = responseStart - navigationStart; DOMready時間 = domContentLoadedEventEnd - navigationStart; onload時間 = loadEventEnd - navigationStart;
如上就是計算方式,爲了方便咱們如今能夠把他們封裝成一個函數,而後把對應的值計算出來,而後咱們根據對應的數據值來進行優化便可。
咱們這邊來封裝下該js的計算方式, 代碼以下:
function getPerformanceTiming() { var performance = window.performance; if (!performance) { console.log('您的瀏覽器不支持performance屬性'); return; } var t = performance.timing; var obj = { timing: performance.timing }; // 重定向耗時 obj.redirectTime = t.redirectEnd - t.redirectStart; // DNS查詢耗時 obj.lookupDomainTime = t.domainLookupEnd - t.domainLookupStart; // TCP連接耗時 obj.connectTime = t.connectEnd - t.connectStart; // HTTP請求耗時 obj.requestTime = t.responseEnd - t.responseStart; // 解析dom樹耗時 obj.domReadyTime = t.domComplete - t.domInteractive; // 白屏時間耗時 obj.whiteTime = t.responseStart - t.navigationStart; // DOMready時間 obj.domLoadTime = t.domContentLoadedEventEnd - t.navigationStart; // 頁面加載完成的時間 即:onload時間 obj.loadTime = t.loadEventEnd - t.navigationStart; return obj; } var obj = getPerformanceTiming(); console.log(obj);
三:前端性能如何優化?
1. 在網頁中,css資源文件儘可能內聯,不要外鏈,具體緣由上面已經有說明。
2. 重定向優化,重定向有301(永久重定向)、302(臨時重定向)、304(Not Modified)。前面兩種重定向儘可能避免。304是用來作緩存的。重定向會耗時。
3. DNS優化,如何優化呢?通常有兩點:第一個就是減小DNS的請求次數,第二個就是進行DNS的預獲取(Prefetching)。在PC端正常的解析DNS一次須要耗費 20-120毫秒的時間。所以咱們能夠減小DNS解析的次數,進而就會減小DNS解析的時間。
第二個就是DNS的預獲取,什麼叫預獲取呢?DNS預獲取就是瀏覽器試圖在用戶訪問連接以前解析域名。好比說咱們網站中有不少連接,可是這些連接不在同一個域名下,咱們能夠在瀏覽器加載的時候就先解析該域名,當用戶真正去點擊該預解析的該連接的時候,能夠平均減小200毫秒的耗時(是指第一次訪問該域名的時候,沒有緩存的狀況下)。這樣就能減小用戶的等待時間,提升用戶體驗。
咱們下面能夠看下淘寶的官網的代碼以下就進行了DNS的Prefetch了,以下所示:
DNS Prefetch 應該儘可能的放在網頁的前面,推薦放在 <meta charset="UTF-8"> 後面。具體使用方法以下:
<link rel="dns-prefetch" href="//xxx.abc.com"> <link rel="dns-prefetch" href="//yyy.def.com"> <link rel="dns-prefetch" href="//bdimg.share.zhix.net">
4. TCP請求優化
TCP的優化就是減小HTTP的請求數量。好比前端資源合併,圖片,資源文件進行壓縮等這些事情。
在http1.0當中默認使用短連接,也就是客戶端和服務端進行一次http請求的時候,就會創建一次連接,任務結束後就會中斷該連接。那麼在這個過程中會有3次TCP請求握手和4次TCP請求釋放操做。
在http1.1中,在http響應頭會加上 Connection: keep-alive,該代碼的含義是:當該網頁打開完成以後,連接不會立刻關閉,
當咱們再次訪問該連接的時候,會繼續使用這個長鏈接。這樣就減小了TCP的握手次數和釋放次數。只須要創建一次TCP連接便可,好比咱們看下百度的請求以下所示:
5. 渲染優化
在咱們作vue或react項目時,咱們常見的模板頁面是經過js來進行渲染的。而不是同構直出的html頁面,對於這個渲染過程當中對於咱們首屏就會有很大的損耗,白屏的時間會增長。所以咱們可使用同構直出的方式來進行服務器端渲染html頁面會比較好,或者咱們可使用一些webpack工具進行html同構直出渲染,webpack渲染能夠看這篇文章。
四:Performance中方法
首先咱們在控制檯中打印下 performance 中有哪些方法,以下代碼:
var performance = window.performance; console.log(performance);
以下所示:
4.1 performance.getEntries()
該方法包含了全部靜態資源的數組列表。
好比我如今html代碼以下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>performance演示</title> </head> <body> <h1>計算時間</h1> <img src="http://img.alicdn.com/tps/TB1EMhjIpXXXXaPXVXXXXXXXXXX.jpg" /> <script type="text/javascript"> window.onload = function() { var performance = window.performance; console.log(performance); console.log(performance.getEntries()) } </script> </body> </html>
如上代碼,我頁面上包含一張淘寶cdn上的一張圖片,由於該方法會獲取頁面中全部包含了頁面中的 HTTP 請求。
而後咱們在瀏覽器中看到打印performance.getEntries()信息以下:
該對象的屬性中除了包含資源加載時間,還有以下幾個常見的屬性:
name: 資源名稱,是資源的絕對路徑,如上圖就是淘寶cdn上面的圖片路徑。咱們能夠經過 performance.getEntriesByName(name屬性值),來獲取該資源加載的具體屬性。
startTime: 開始時間
duration: 表示加載時間,它是一個毫秒數字,只能獲取同域下的時間點,若是是跨域的話,那麼該時間點爲0。
entryType: 資源類型 "resource", 還有 "navigation", "mark" 和 "measure" 這三種。
initiatorType: 表示請求的來源的標籤,好比 link標籤、script標籤、img標籤等。
所以咱們能夠像 getPerformanceTiming 該方法同樣,封裝一個方法,獲取某個資源的時間。以下封裝代碼方法以下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>performance演示</title> </head> <body> <h1>計算時間</h1> <img src="http://img.alicdn.com/tps/TB1EMhjIpXXXXaPXVXXXXXXXXXX.jpg" /> <script type="text/javascript"> // 計算加載時間 function getEntryTiming (entry) { var obj = {}; // 獲取重定向的時間 obj.redirectTime = entry.redirectEnd - entry.redirectStart; // 獲取DNS查詢耗時 obj.lookupDomainTime = entry.domainLookupEnd - entry.domainLookupStart; // 獲取TCP連接耗時 obj.connectTime = entry.connectEnd - entry.connectStart; // HTTP請求耗時 obj.requestTime = entry.responseEnd - entry.responseStart; obj.name = entry.name; obj.entryType = entry.entryType; obj.initiatorType = entry.initiatorType; obj.duration = entry.duration; return obj; } window.onload = function() { var entries = window.performance.getEntries(); console.log(entries); entries.forEach(function(item) { if (item.initiatorType) { var curItem = getEntryTiming(item); console.log(curItem); } }); } </script> </body> </html>
而後如上會有2個console.log 打印數據,咱們到控制檯中看到打印信息以下:
如上圖所示,咱們能夠看到經過 getEntryTiming 方法計算後,會拿到各對應的值。
4.2 performance.now()
該方法會返回一個當前頁面執行的時間的時間戳,能夠用來精確計算程序執行的實際。
好比以下,我循環100萬次,而後返回一個數組,咱們來看下代碼以下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>performance演示</title> </head> <body> <h1>計算時間</h1> <img src="http://img.alicdn.com/tps/TB1EMhjIpXXXXaPXVXXXXXXXXXX.jpg" /> <script type="text/javascript"> function doFunc() { var arrs = []; for (var i = 0; i < 1000000; i++) { arrs.push({ 'label': i, 'value': i }); } return arrs; } var t1 = window.performance.now(); console.log(t1); doFunc(); var t2 = window.performance.now(); console.log(t2); console.log('doFunc函數執行的時間爲:'+ (t2 - t1) + '毫秒'); </script> </body> </html>
而後咱們再來打印下 doFunc() 這個函數執行了多久,以下所示:
咱們也知道咱們還有一個時間就是 Date.now(), 可是performance.now()與Date.now()方法不一樣的是:
該方法使用了一個浮點數,返回的是以毫秒爲單位,小數點精確到微妙級別的時間。相對於Date.now() 更精確,而且不會受系統程序堵塞的影響。
下面咱們來看看使用 Date.now()方法使用的demo以下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>performance演示</title> </head> <body> <h1>計算時間</h1> <img src="http://img.alicdn.com/tps/TB1EMhjIpXXXXaPXVXXXXXXXXXX.jpg" /> <script type="text/javascript"> function doFunc() { var arrs = []; for (var i = 0; i < 1000000; i++) { arrs.push({ 'label': i, 'value': i }); } return arrs; } var t1 = Date.now(); console.log(t1); doFunc(); var t2 = Date.now(); console.log(t2); console.log('doFunc函數執行的時間爲:'+ (t2 - t1) + '毫秒'); </script> </body> </html>
執行的結果以下:
注意:performance.timing.navigationStart + performance.now() 約等於Date.now();
4.3 performance.mark()
該方法的含義是用來自定義添加標記的時間, 方便咱們計算程序的運行耗時。該方法使用以下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>performance演示</title> </head> <body> <h1>計算時間</h1> <img src="http://img.alicdn.com/tps/TB1EMhjIpXXXXaPXVXXXXXXXXXX.jpg" /> <script type="text/javascript"> function doFunc() { var arrs = []; for (var i = 0; i < 1000000; i++) { arrs.push({ 'label': i, 'value': i }); } return arrs; } // 函數執行前作個標記 var mStart = 'mStart'; var mEnd = 'mEnd'; window.performance.mark(mStart); doFunc(); // 函數執行以後再作個標記 window.performance.mark(mEnd); // 而後測量這兩個標記之間的距離,而且保存起來 var name = 'myMeasure'; window.performance.measure(name, mStart, mEnd); // 下面咱們經過 performance.getEntriesByName 方法來獲取該值 console.log(performance.getEntriesByName('myMeasure')); console.log(performance.getEntriesByType('measure')); </script> </body> </html>
如上代碼,咱們經過 window.performance.measure(name, mStart, mEnd); 這個方法作出標記後,咱們可使用performance.getEntriesByName('myMeasure') 和 performance.getEntriesByType('measure') 獲取該值。
以下圖所示:
4.4 performance.getEntriesByType()
該方法返回一個 PerformanceEntry 對象的列表,基於給定的 entry type, 如上代碼 performance.getEntriesByType('measure') 就能夠獲取該值。
4.5 performance.clearMeasures()
從瀏覽器的性能輸入緩衝區中移除自定義添加的 measure. 代碼以下所示:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>performance演示</title> </head> <body> <h1>計算時間</h1> <img src="http://img.alicdn.com/tps/TB1EMhjIpXXXXaPXVXXXXXXXXXX.jpg" /> <script type="text/javascript"> function doFunc() { var arrs = []; for (var i = 0; i < 1000000; i++) { arrs.push({ 'label': i, 'value': i }); } return arrs; } // 函數執行前作個標記 var mStart = 'mStart'; var mEnd = 'mEnd'; window.performance.mark(mStart); doFunc(); // 函數執行以後再作個標記 window.performance.mark(mEnd); // 而後測量這兩個標記之間的距離,而且保存起來 var name = 'myMeasure'; window.performance.measure(name, mStart, mEnd); // 下面咱們經過 performance.getEntriesByName 方法來獲取該值 console.log(performance.getEntriesByName('myMeasure')); console.log(performance.getEntriesByType('measure')); // 使用 performance.clearMeasures() 方法來清除 自定義添加的 measure performance.clearMeasures(); console.log(performance.getEntriesByType('measure')); </script> </body> </html>
如上咱們在最後代碼中使用 performance.clearMeasures() 方法清除了全部自定義的measure。而後咱們後面從新使用 console.log(performance.getEntriesByType('measure'));打印下,看到以下信息:
4.6 performance.getEntriesByName(name屬性的值)
該方法返回一個 PerformanceEntry 對象的列表,基於給定的 name 和 entry type。
4.7 performance.toJSON()
該方法是一個 JSON 格式轉化器,返回 Performance 對象的 JSON 對象。以下代碼所示:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>performance演示</title> </head> <body> <h1>計算時間</h1> <img src="http://img.alicdn.com/tps/TB1EMhjIpXXXXaPXVXXXXXXXXXX.jpg" /> <script type="text/javascript"> console.log(window.performance); var js = window.performance.toJSON(); console.log("json = " + JSON.stringify(js)); </script> </body> </html>
而後打印信息以下:
五:使用performane編寫小工具
首先html使用代碼以下(切記必定要把初始代碼放到window.onload裏面,由於確保圖片加載完成):
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>performance演示</title> </head> <body> <h1>計算時間</h1> <img src="http://img.alicdn.com/tps/TB1EMhjIpXXXXaPXVXXXXXXXXXX.jpg" /> <img src="https://aecpm.alicdn.com/simba/img/TB1XotJXQfb_uJkSnhJSuvdDVXa.jpg" /> <script type="text/javascript" src="./js/performance.js"></script> <script type="text/javascript"> window.onload = function() { window.performanceTool.getPerformanceTiming(); }; </script> </body> </html>
而後頁面進行預覽效果以下:
如上圖咱們就能夠很清晰的能夠看到,頁面的基本信息了,好比:重定向耗時、Appcache耗時、DNS查詢耗時、TCP連接耗時、HTTP請求耗時、請求完畢到DOM加載耗時、解析DOM樹耗時、白屏時間耗時、load事件耗時、及 頁面加載完成的時間。頁面加載完成的時間 是上面全部時間的總和。及下面,咱們也能夠分別清晰的看到,js、css、image、video、等信息的資源加載及總共用了多少時間。
js基本代碼以下:
src/utils.js 代碼以下:
export function isObject(obj) { return obj !== null && (typeof obj === 'object') } // 格式化成毫秒 export function formatMs(time) { if (typeof time !== 'number') { console.log('時間必須爲數字'); return; } // 毫秒轉換成秒 返回 if (time > 1000) { return (time / 1000).toFixed(2) + 's'; } // 默認返回毫秒 return Math.round(time) + 'ms'; } export function isImg(param){ if (/\.(gif|jpg|jpeg|png|webp|svg)/i.test(param)) { return true; } return false; } export function isJS(param){ if (/\.(js)/i.test(param)) { return true; } return false; } export function isCss(param){ if (/\.(css)/i.test(param)) { return true; } return false; } export function isVideo(param){ if (/\.(mp4|rm|rmvb|mkv|avi|flv|ogv|webm)/i.test(name)) { return true; } return false; } export function checkResourceType(param){ if (isImg(param)) { return 'image'; } if (isJS(param)) { return 'javascript'; } if (isCss(param)) { return 'css'; } if (isVideo(param)) { return 'video'; } return 'other' }
js/index.js 代碼以下:
var utils = require('./utils'); var formatMs = utils.formatMs; var isObject = utils.isObject; var checkResourceType = utils.checkResourceType; function Performance() {}; Performance.prototype = { // 獲取數據信息 getPerformanceTiming: function() { // 初始化數據 this.init(); if (!isObject(this.timing)) { console.log('值須要是一個對象類型'); return; } // 過早獲取 loadEventEnd值會是0 var loadTime = this.timing.loadEventEnd - this.timing.navigationStart; if (loadTime < 0) { setTimeout(() => { this.getPerformanceTiming(); }, 200); return; } // 獲取解析後的數據 this.afterDatas.timingFormat = this._setTiming(loadTime); this.afterDatas.enteriesResouceDataFormat = this._setEnteries(); this._show(); }, init: function() { this.timing = window.performance.timing; // 獲取資源類型爲 resource的全部數據 this.enteriesResouceData = window.performance.getEntriesByType('resource'); }, // 保存原始數據 timing: {}, // 原始enteries數據 enteriesResouceData: [], // 保存解析後的數據 afterDatas: { timingFormat: {}, enteriesResouceDataFormat: {}, enteriesResouceDataTiming: { "js": 0, "css": 0, "image": 0, "video": 0, "others": 0 } }, _setTiming: function(loadTime) { var timing = this.timing; // 對數據進行計算 var data = { "重定向耗時": formatMs(timing.redirectEnd - timing.redirectStart), "Appcache耗時": formatMs(timing.domainLookupStart - timing.fetchStart), "DNS查詢耗時": formatMs(timing.domainLookupEnd - timing.domainLookupStart), "TCP連接耗時": formatMs(timing.connectEnd - timing.connectStart), "HTTP請求耗時": formatMs(timing.responseEnd - timing.responseStart), "請求完畢到DOM加載耗時": formatMs(timing.domInteractive - timing.responseEnd), "解析DOM樹耗時": formatMs(timing.domComplete - timing.domInteractive), "白屏時間耗時": formatMs(timing.responseStart - timing.navigationStart), "load事件耗時": formatMs(timing.loadEventEnd - timing.loadEventStart), "頁面加載完成的時間": formatMs(loadTime) }; return data; }, _setEnteries: function() { var enteriesResouceData = this.enteriesResouceData; var imageArrs = [], jsArrs = [], cssArrs = [], videoArrs = [], otherArrs = []; enteriesResouceData.map(item => { var d = { '資源名稱': item.name, 'HTTP協議類型' : item.nextHopProtocol, "TCP連接耗時" : formatMs(item.connectEnd - item.connectStart), "加載時間" : formatMs(item.duration) }; switch(checkResourceType(item.name)) { case 'image': this.afterDatas.enteriesResouceDataTiming.image += item.duration; imageArrs.push(d); break; case 'javascript': this.afterDatas.enteriesResouceDataTiming.js += item.duration; jsArrs.push(d); break; case 'css': this.afterDatas.enteriesResouceDataTiming.css += item.duration; cssArrs.push(d); break; case 'video': this.afterDatas.enteriesResouceDataTiming.video += item.duration; videoArrs.push(d); break; case 'others': this.afterDatas.enteriesResouceDataTiming.others += item.duration; otherArrs.push(d); break; } }); return { 'js': jsArrs, 'css': cssArrs, 'image': imageArrs, 'video': videoArrs, 'others': otherArrs } }, _show: function() { console.table(this.afterDatas.timingFormat); for( var key in this.afterDatas.enteriesResouceDataFormat ){ console.group(key + "--- 共加載時間" + formatMs(this.afterDatas.enteriesResouceDataTiming[key])); console.table(this.afterDatas.enteriesResouceDataFormat[key]); console.groupEnd(key); } } }; var Per = new Performance(); module.exports = Per;
注意:把github源碼下載完成後,須要 執行 npm run build 打包,打包完成後,把dist/下的js文件複製到本身項目中便可,而後在項目中引入該js文件,而後調用便可在控制檯中看到一些信息效果。
注:基本代碼也參考了github上一些代碼。這些不重要,重要的是學到東西,而且在項目中能用起來。提升效率。