Web Performance Timing API
簡介
想象一下您正在訪問咱們的W3C網站,若是Web內容在必定時間內沒有顯示在屏幕上,那麼做爲用戶,也許您只是關閉選項卡,而後轉到其餘選項。可是,做爲開發人員,您可能但願跟蹤請求和導航詳細信息中的提示,以便找出致使此網頁速度降低的緣由。javascript
幸運的是,大多數瀏覽器供應商都願意公開容許開發人員收集準確性能細節的性能特徵。顧名思義,這些性能計時API有助於從不一樣方面識別Web應用程序的瓶頸並提升性能。css
當試圖瞭解Web應用程序的性能時,瀑布圖多是您會想到的第一個工具。您是否想過了解這些圖形圖表背後的魔力?在如下各節中,咱們將解釋一組性能監視API,這些API使您不只能夠執行瀑布圖測量。html
資源時效【Resource Timing】
上圖是一個簡單網頁每一個加載資源的時間數據的瀑布圖。實際上,[RESOURCE-TIMING] API 能夠提供比這更多的詳細信息。圖2是開發人員可以訪問的 Web 應用程序中每一個加載資源的一組屬性。前端
資源的加載時間【Load Times for Resources 】
Web 應用程序大多由一組可下載的資源組成。這裏的資源一般引用 HTML 文檔、 XHR 對象、連接(例如樣式表)或 SVG 元素。java
Resources Timing
數據做爲全局 window.performance
對象上的方法公開,咱們將在下一節討論這個問題。建議您使用 performance.getEntriesByType("Resource")
方法爲每一個請求的資源獲取一個資源計時對象數組。web
Performanceresourcetiming
接口擴展了 Performance Timeline
中的 PerformanceEntry
接口。資源計時中的屬性能夠用 responseEnd
和 startTime
之間的差值來計算所用的時間。面試
如圖2和圖3所示,您能夠訪問頁面上每一個資源的一組關鍵網絡計時屬性。算法
每一個時間戳都以微秒爲單位,由 High Resolution Time
規範中的 window.performance.now()
方法提供。shell
例子【Example】
舉個例子,讓咱們來測量一下獲取 W3C 主頁上的圖標所需的時間。api
<html> <head> </head> <body onload="loadResources()"> <script> function loadResources(){ var image1 = new Image(); image1.onload = resourceTiming; image1.src = 'https://www.w3.org/Icons/w3c_main.png'; }
function resourceTiming(){ var resourceList = window.performance.getEntriesByType("resource"); for (var i = 0; i < resourceList.length; i++) { if (resourceList[i].initiatorType == "img") { alert("End to end resource fetch: " + (resourceList[i].responseEnd - resourceList[i].startTime)); } } }</script> <img id="image0" src="https://www.w3.org/Icons/w3c_home.png"> </body></html>
瀏覽器兼容【Browser Support】
到目前爲止,[RESOURCE-TIMING] API 已獲得普遍實現。圖4顯示了主要的桌面和移動網絡瀏覽器的當前支持數據,請訪問 caniuse.com 瞭解最新狀態。
執行時間線【Performance Timeline】
概要【Overview】
您可能已經開始思考,這些網頁上資源的計時數據是否足夠好,以衡量其性能?是的,爲了理解你的 web 應用程序,老是有必要探索更多: 頁面導航、用戶交互、請求-響應循環等。根據這一要求,Web 性能工做組引入了性能時間表,這是一個統一的接口,用於獲取各類性能指標。
EntryTypePerformance Timeline API 使用 PerformanceEntry.entryType 來描述這個 PerformanceEntry對象所表示的接口類型,它表示測算的性能(時間)。var entryType = performance.getEntries()[0].entryType// Entry Type
每一個 PerformanceEntry
對象都公開如下繼承的屬性:
performance.getEntries
performance.getEntries
用來獲取當前時間調用前,瀏覽器從進入該頁面開始的全部事件類型的詳細時間數據,返回是一個Array<PerformanceEntry>
name(名稱)
這個屬性的 DOMString
標識符對象,不必定是惟一的。
entryType(類型)
描述由此表示DOMString
接口類型的 PerformanceEntry
對象。
startTime(開始時間)
此性能指標的第一個記錄時間戳的時間值。
duration(所用時間)
此記錄的整個事件持續時間的時間值
W3c WebPerf WG 維護 PerformanceEntry.entryType 的已知值列表。
entryType 的值
分析較長的用時【High-Resolution Time】
前言【Introduction】
參與 Performance Timeline
的全部指標都可以以亞毫秒級的分辨率提供計時數據,即 DOMHighResTimeStamp
,它由高分辨率時間規範[ HR-TIME-2]定義。
單調計時【Monotonic Clock】
在設計的早期階段,咱們的計時 api 是根據牆上時鐘的時間來定義的。不幸的是,在現代計算機系統中,這樣的時間有一個使人不快的特性: 它們並非以時間實際流逝的速度單調地增加。特別是,NTP 調整、用戶配置更改等等會致使系統報告的時間向後、向前太快或向前太慢。
例如,能夠在到 Date.now()
的兩個調用之間登陸正數、負數或零。
var mark_start = Date.now();doTask(); // Some taskif (window.console) window.console.log('Duration of task: ' + (Date.now() - mark_start));
High-Resolution Time
的概念是提供一個單調的、均勻遞增的時間戳,適用於區間測量,這是基於如下規則而實現的:
now()
方法時返回的時間值和Performance
方法,工做表現對象具備相同時間起源【time origin】 必須不受系統時鐘調整或系統時鐘偏斜的影響。now()
方法返回的任意兩個按時間順序記錄的時間值之間的差值,若是兩個時間值具備相同的值,則永遠不要爲負數
亞毫秒分析【Sub-millisecond Resolution】
Now()
在肯定日曆時間的當前值方面確實很是有用,並且使用歷史悠久。然而,從長遠來看,須要更高的精確度。
圖形就是一個例子。在計算基於腳本的動畫的幀速率時,開發人員須要亞毫秒的分辨率來肯定一個動畫是否在60 FPS 下繪製。若是沒有亞毫秒級的分辨率,開發人員只能肯定動畫是以58.8 FPS 仍是62.5 FPS 繪製。
Domhighrestimestamp
類型和 Performance
接口的now()
方法經過提供亞毫秒級分辨率的時間值來解決本節中總結的問題。
時間起源【Time Origin】
時間起源肯定時間值從哪一個時間被測量。對於專用工做線程或文檔,起始時間是該文檔頁面導航的開始時間,對於共享工做線程,起始時間是建立共享工做線程的時間。爲了更準確的定義,請檢查規範的細節。
假設咱們有一個共享工做線程 a,它是在父文檔的導航開始後10ms 建立的。若是在工做線程和父上下文中建立共享工做線程 a 以後,咱們稱之爲 performance.now()
要用時5ms,那麼咱們應該看到如下值:
Shared worker A:performance.now(): 5.000 ms
Parent context:performance.now(): 15.000 ms
要在相同的時間線上顯示這樣的事件,應用程序可使用 translateTime
方法從工做線上轉換 DOMHighResTimeStamps
。
// ---- worker.js -----------------------------// Shared worker scriptonconnect = function(e) { var port = e.ports[0]; port.onmessage = function(e) { // Time execution in worker var task_start = performance.now(); result = runSomeWorkerTask(); var task_end = performance.now();
port.postMessage({ 'task': 'Some worker task', 'start_time': task_start, 'end_time': task_end, 'result': result }); }}
// ---- application.js ------------------------// Timing tasks in the documentvar task_start = performance.now();result = runSomeWorkerTask();var task_end = performance.now();
plotEventOnTimeline({ 'task': 'Some document task', 'start_time': task_start, 'end_time': task_end, 'result': result});
// Translating worker timestamps into document's time originvar worker = new SharedWorker('worker.js');worker.port.onmessage = function (event) { var msg = event.data;
// translate timestamps into document's time origin msg.start_time = performance.translateTime(msg.start_time, worker); msg.end_time = performance.translateTime(msg.end_time, worker);
// plot the results on document's timeline plotEventOnTimeline(msg);}
將 originTime
添加到 performance.now()
中,就會獲得一個在任何上下文中均可以比較的時間值。如今咱們能夠獲得這樣的結果:
Shared worker A:performance.now(): 5.000 msperformance.originTime: 110.000 msperformance.originTime + performance.now(): 115.000 ms
Parent context:performance.now(): 15.000 msperformance.originTime: 100.000 msperformance.originTime + performance.now(): 115.000 ms
導航時間【Navigation Timing】
如今咱們知道了如何獲取單個資源的計時指標,接下來讓咱們進一步訪問文檔導航的完整計時信息。
導航時間、性能時間線和資源時間【Navigation Timing, Performance Timeline and Resource Timing】
導航是關於用戶代理如何將請求的 HTML
、 CSS
和 JavaScript
轉換爲渲染的像素,這是用戶瀏覽文檔最關鍵的步驟之一。
導航計時 API 是 Web 性能 API 的起點。在[ NAVIGATION-TIMING ]中,經過訪問 window.performance.navigation
,您將得到一個 PerformanceNavigationTiming
實例,該實例提供有關頁面性能的與時間相關的信息。這不符合經過performance.getEntries
提供統一入口的目標。到2011年引入性能時間軸 api 時,導航計時的初始設計已經被普遍實現,所以要將其與性能時間軸對齊爲時已晚。
導航計時 API Level 2
試圖經過一個理想的導航計時設計來修復這個歷史性的錯誤。它參與 Performance Timeline API
,並從 PerformanceResourceTiming
接口擴展 initialatortype
和 workerStart
。
PerformanceNavigationTiming 的屬性
圖5顯示了在[ NAVIGATION-TIMING-2]中定義的頁面的導航的關鍵性能特徵列表。
下面是瀏覽 www.w3.org 網頁的演示。
瀏覽器兼容【Browser Support】
圖7是由 caniuse 網站生成的支持數據表[ NAVIGATION-TIMING]。
用戶時間【User Timing】
到目前爲止,經過導航時序和資源時序,您能夠充分訪問資源加載和頁面導航中那些關鍵時刻的時序信息。可是,若是您想肯定用戶的按鈕點擊交互出了什麼問題,該怎麼辦?是否有可能得到對你來講很重要的我的任務的高精度性能特徵?
User Timing
是 Performance
接口的擴展,能夠經過提供高精度的時間戳幫助您度量應用程序的性能。
這裏有一個簡單的示例,解釋了開發人員如何使用本文中定義的接口來獲取與開發人員腳本相關的計時數據。
<html> <head> <title>User Timing example</title> </head> <body onload="init()"> <script> function init(){ performance.mark("startTask1"); doTask1(); // Some developer code performance.mark("endTask1");
performance.mark("startTask2"); doTask2(); // Some developer code performance.mark("endTask2");
measurePerf(); }
function measurePerf(){ var perfEntries = performance.getEntriesByType("mark"); for (var i = 0; i < perfEntries.length; i++) { if (window.console) console.log("Name: " + perfEntries[i].name + " Entry Type: " + perfEntries[i].entryType + " Start Time: " + perfEntries[i].startTime + " Duration: " + perfEntries[i].duration + "\n"); } }</script> </body></html>
手動打點【performance.mark()】
Performancemark
接口擴展了 Performance
接口,並經過一個名爲 mark()
的函數向用戶公開標記。
Mark()
容許 web 開發人員在他們的 web 應用程序中建立獨特的標記,並使用 DOMString markName
自定義標記。例如,要建立一個名爲before click
的標記,請調用 window.performance.mark("before click」)
;
您可使用 Performance
接口訪問已經存儲的標記。經過調用 window.performance.getEntriesByType(" mark")
,您將獲得應用程序中全部標記的列表。
當標記再也不具備任何價值時,能夠刪除它們。您能夠經過調用 clearMarks()
來刪除全部的標記,或者經過調用 clearMarks("mark to delete")
來刪除一個標記、標記要刪除。
計算點之間的耗時【performance.measure()】
當您準備了足夠的標記(兩個點+)時,PerformanceMeasure
接口能夠幫助您計算兩個標記之間的運行時間。此接口也是 Performance
接口的擴展,該接口公開經過 measure()
方法建立的度量。在這裏,measure ()存儲兩個標記之間的 DOMHighResTimeStamp
持續時間以及相關的名稱("measure")
。例如,要度量連續的單擊,咱們能夠調用 window.performance.measure("measure click","click before","click after")
。
與 PerformanceMark
接口相似,只需調用 window.performance.getEntriesByType("measure")
就能夠得到度量值,並使用 clearMeasures()
方法刪除度量值。
瀏覽器兼容【Browser Support】
如今,你能夠在大多數主流瀏覽器上使用 User Timing API。請訪問 caniuse 網站獲取最新的瀏覽器支持信息。
應用場景
檢測 WEB 應用的各依賴資源的加載時間,並上報到服務器進行分析
對耗時可能較長的方法函數進行耗時監聽,並上傳服務器進行分析
分析用戶從進入網頁開始到頁面呈現的全部時間,而且上傳服務器分析首屏渲染耗時
當拿到上述的數據後,咱們就能夠對數據進行分析,知道咱們的程序到底慢在哪裏,而後作正對性的優化,而不像原來沒有數據的支撐致使咱們無從下手優化工做。
以上是我對英版原版文檔的翻譯,若是有翻譯不對的地方還請諒解。
2020年4月1日
Jboss(基老闆)
做者:JBoss連接:https://juejin.im/post/5e849e6de51d4546e14f4304
感謝閱讀❤️
歡迎關注「前端瓶子君」,回覆「交流」加入前端交流羣!
歡迎關注「前端瓶子君」,回覆「算法」自動加入,從0到1構建完整的數據結構與算法體系!
在這裏,瓶子君不只介紹算法,還將算法與前端各個領域進行結合,包括瀏覽器、HTTP、V八、React、Vue源碼等。
在這裏,你能夠天天學習一道大廠算法題(阿里、騰訊、百度、字節等等)或 leetcode,瓶子君都會在次日解答喲!
「在看轉發」是最大的支持
本文分享自微信公衆號 - 前端瓶子君(pinzi_com)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。