假設您正在訪問一個網站,若是Web內容不在幾秒內顯示在屏幕上,那麼做爲用戶您可能會選擇關閉標籤頁,轉去瀏覽其餘頁面從而代替這個網頁的內容。可是做爲Web開發者,您可能但願跟蹤請求與導航的詳細信息,這樣你就能夠知道爲何這個網頁的速度在變慢。javascript
W3C性能工做組提供了能夠用來測量和改進Web應用性能的用戶代理(User Agent)特性與API。開發者可使用這些API來收集精確的性能信息,從不一樣方面找出Web應用的性能瓶頸並提升應用的性能。css
這些特性和API適用於桌面、移動瀏覽器以及其餘非瀏覽器環境。html
因爲這些特性與API不止適用於瀏覽器,還適用於非瀏覽器環境,因此本文會大量使用「用戶代理」這個詞來代替「瀏覽器」前端
ECMA-262 規範中定義了 Date
對象來表示自 1970 年 1 月 1 日以來的毫秒數。它足以知足大部分需求,但缺點是時間會受到時鐘誤差與系統時鐘調整的影響。時間的值不老是單調遞增,後續值有可能會減小或者保持不變。java
例如,下面這段代碼計算出來的「duration
」有可能被記錄爲正數、負數或零。git
const mark_start = Date.now()
doTask() // Some task
const duration = Date.now() - mark_start
複製代碼
上面這段代碼獲取的持續時間「duration」並不精準,它會受到時鐘誤差與系統時鐘調整的影響,因此最終獲得的「duration」可能爲正數、負數或零,咱們根本不知道它記錄的時間到底是不是正確的時間。github
高精度時間(High Resolution Time,簡稱hr-time
)規範定義了Performance
對象,經過Performance
對象咱們能夠得到高精度的時間。canvas
Performance
對象包含方法now
和屬性timeOrigin
:跨域
方法now
被執行後會返回從 timeOrigin
到如今的高精度時間。數組
當前時間 - performance.timeOrigin
屬性timeOrigin
返回頁面瀏覽上下文第一次被建立的時間。若是全局對象爲WorkerGlobalScope
,那麼timeOrigin
爲worker被建立的時間。
timeOrigin 的時間值不受時鐘誤差與系統時鐘調整的影響。
例如,當timeOrigin
的值被肯定以後,不管將系統時間設置到什麼時間,下面代碼始終返回timeOrigin
最初被賦予的時間:
new Date(performance.timeOrigin).toLocaleString()
// 2018/8/6 上午11:41:58
複製代碼
若是兩個時間值擁有相同的時間起源(Time Origin),那麼使用 performance.now
方法返回的任意兩個按時間順序記錄的時間值之間的差值永遠不多是負數。
例如,下面這段代碼計算出來的「duration
」永遠不可能爲負數。
const mark_start = performance.now()
doTask() // Some task
const duration = performance.now() - mark_start
複製代碼
經過performance.timeOrigin
+ performance.now
能夠獲得精準的當前時間。該時間不受時鐘誤差與系統時鐘調整的影響。
不受時鐘誤差與系統時鐘調整的影響指的是當
timeOrigin
的值被肯定以後修改了系統時間,這時候timeOrigin
不會受到影響。
const timeStamp = performance.timeOrigin + performance.now()
console.log(timeStamp) // 1533539552977.5718
new Date(timeStamp).toLocaleString()
// "2018/8/6 下午3:10:42"
複製代碼
在介紹如何獲取性能指標以前,咱們須要先介紹「性能時間線」,它提供了統一的接口來獲取各類性能相關的度量數據。它是本文即將要介紹的其餘獲取性能指標方法的基礎。
「性能時間線」自己並不提供任何性能信息,但它會提供一些方法,當您想要得到性能信息時,可使用「性能時間線」提供的方法來獲得您想獲取的性能信息。
本文後面會詳細介紹從「性能時間線」中能夠訪問哪些性能信息
Performance
對象「性能時間線」擴展了Performance
對象,新增了一些用於從「性能時間線」中獲取性能指標數據的屬性與方法。
下表給出了在Performance
對象上新增的方法:
方法名 | 做用 |
---|---|
getEntries() | 返回一個列表,該列表包含一些用於承載各類性能數據的對象,不作任何過濾 |
getEntriesByType() | 返回一個列表,該列表包含一些用於承載各類性能數據的對象,按類型過濾 |
getEntriesByName() | 返回一個列表,,該列表包含一些用於承載各類性能數據的對象,按名稱過濾 |
表中給出了三個方法,使用這些方法能夠獲得一個列表,列表中包含一系列用於承載各類性能數據的對象。換句話說,使用這些對象能夠獲得咱們想要得到的各類性能信息。
在術語上這個列表叫作PerformanceEntryList
,而列表中的對象叫作PerformanceEntry
。
不一樣方法的過濾條件不一樣,因此列表中的PerformanceEntry
對象所包含的數據也不一樣。
PerformanceEntry
對象「性能時間線」定義了PerformanceEntry
對象,該對象承載了各類性能相關的數據。下表給出了PerformanceEntry
對象所包含的屬性:
屬性名 | 做用 |
---|---|
name | 經過該屬性能夠獲得PerformanceEntry 對象的標識符,不惟一 |
entryType | 經過該屬性能夠獲得PerformanceEntry 對象的類型 |
startTime | 經過該屬性能夠獲得一個時間戳 |
duration | 經過該屬性能夠獲得持續時間 |
從上表中能夠發現,「性能時間線」並無明肯定義PerformanceEntry
對象應該返回什麼具體內容,它只是定義了一個格式,返回的具體內容會根據咱們獲取的性能數據類型的不一樣而不一樣。本文的後面咱們會詳細介紹。
PerformanceObserver
「性能時間線」還定義了一個很是重要的接口用來觀察「性能時間線」記錄新的性能信息,當一個新的性能信息被記錄時,觀察者將會收到通知。它就是PerformanceObserver
。例如,能夠經過下面代碼定義一個長任務觀察者:
const observer = new PerformanceObserver(function (list) {
// 當記錄一個新的性能指標時執行
})
// 註冊長任務觀察者
observer.observe({entryTypes: ['longtask']})
複製代碼
上面這段代碼使用PerformanceObserver
註冊了一個長任務觀察者,當一個新的長任務性能信息被記錄時,回調會被觸發。
回調函數會接收到兩個參數:第一個參數是一個列表,第二個參數是觀察者實例。
在術語上這個列表被稱爲PerformanceObserverEntryList
,而且包含三個方法getEntries
、getEntriesByType
、getEntriesByName
。能夠經過這三個方法得到PerformanceEntryList
列表。這三個方法功能於使用方式均與前面介紹的相同。
獲取資源加載相關的時間信息可讓咱們知道咱們的頁面須要讓用戶等待多久。下面這段簡單的JavaScript代碼嘗試測量加載資源所需的時間:
<!doctype html>
<html>
<head></head>
<body onload="loadResources()">
<script> function loadResources() { const start = new Date().getTime() const image1 = new Image() const resourceTiming = function() { const now = new Date().getTime() const latency = now - start console.log('End to end resource fetch: ' + latency) } image1.onload = resourceTiming image1.src = 'https://www.w3.org/Icons/w3c_main.png' } </script>
<img src="https://www.w3.org/Icons/w3c_home.png">
</body>
</html>
複製代碼
雖然這段代碼能夠測量資源的加載時間,但它不能得到資源加載過程當中各個階段詳細的時間信息。同時這段代碼並不能投放到生產環境,由於它有不少問題:
@import url()
和background: url()
加載的資源應該如何測量計時信息?link
、img
、script
xmlhttprequest
請求的,如何測量資源的計時信息?fetch
方法請求的資源如何測量計時信息?beacon
發送的請求如何測量計時信息?幸運的是,W3C性能工做組定義了資源計時(Resource Timing)規範讓Web開發者能夠獲取很是詳細的資源計時信息。
下面這個例子能夠獲取更加詳細的資源計時信息:
<!doctype html>
<html>
<head>
</head>
<body onload="loadResources()">
<script> function loadResources () { const image1 = new Image() image1.onload = resourceTiming image1.src = 'https://www.w3.org/Icons/w3c_main.png' } function resourceTiming () { const resourceList = window.performance.getEntriesByType('resource') for (let i = 0; i < resourceList.length; i++) { console.log('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>
複製代碼
上面代碼經過performance.getEntriesByType
方法獲得一個列表,這個列表就是咱們前面介紹的PerformanceEntryList
,並過濾出全部類型爲resource
的PerformanceEntry
對象。
類型爲resource
的PerformanceEntry
對象在術語上被稱爲PerformanceResourceTiming
對象。
PerformanceResourceTiming
對象擴展了PerformanceEntry
對象並新增了不少屬性用於獲取詳細的資源計時信息,PerformanceResourceTiming
對象的全部屬性與其對應的做用以下表所示:
屬性名 | 做用 |
---|---|
name | 請求資源的絕對地址,即使請求重定向到一個新的地址此屬性也不會改變 |
entryType | PerformanceResourceTiming 對象的entryType 屬性永遠返回字符串「resource」 |
startTime | 用戶代理開始排隊獲取資源的時間。若是HTTP重定則該屬性與redirectStart 屬性相同,其餘狀況該屬性將與fetchStart 相同 |
duration | 該屬性將返回 responseEnd 與 startTime 之間的時間 |
initiatorType | 發起資源的類型 |
nextHopProtocol | 請求資源的網絡協議 |
workerStart | 若是當前上下文是」worker」,則workerStart 屬性返回開始獲取資源的時間,不然返回0 |
redirectStart | 資源開始重定向的時間,若是沒有重定向則返回0 |
redirectEnd | 資源重定向結束的時間,若是沒有重定向則返回0 |
fetchStart | 開始獲取資源的時間,若是資源重定向了,那麼時間爲最後一個重定向資源的開始獲取時間 |
domainLookupStart | 資源開始進行DNS查詢的時間(若是沒有進行DNS查詢,例如使用了緩存或本地資源則時間等於fetchStart) |
domainLookupEnd | 資源完成DNS查詢的時間(若是沒有進行DNS查詢,例如使用了緩存或本地資源則時間等於fetchStart) |
connectStart | 用戶代理開始與服務器創建用來檢索資源的鏈接的時間(TCP創建鏈接的時間) |
connectEnd | 用戶代理完成與服務器創建的用來檢索資源的鏈接的時間(TCP鏈接成功的時間) |
secureConnectionStart | 如資源使用安全傳輸,那麼用戶代理會啓動握手過程以確保當前鏈接。該屬性表明握手開始時間(若是頁面使用HTTPS那麼值是安全鏈接握手以前的時間) |
requestStart | 開始請求資源的時間 |
responseStart | 用戶代理開始接收Response 信息的時間(開始接受Response 的第一個字節,例如HTTP/2的幀頭或HTTP/1.x的Response狀態行) |
responseEnd | 用戶代理接收到資源的最後一個字節的時間,或在傳輸鏈接關閉以前的時間,使用先到者的時間。或者是因爲網絡錯誤而終止網絡的時間 |
transferSize | 表示資源的大小(以八位字節爲單位),該大小包括響應頭字段和響應有效內容主體(Payload Body) |
encodedBodySize | 表示從HTTP網絡或緩存中接收到的有效內容主體(Payload Body)的大小(在刪除全部應用內容編碼以前) |
decodedBodySize | 表示從HTTP網絡或緩存中接收到的消息主體(Message Body)的大小(在刪除全部應用內容編碼以後) |
因爲有一些屬性功能比較複雜,下面將針對一些功能比較複雜的屬性詳細介紹。
initiatorType
簡單來講initiatorType
屬性返回的內容表明資源是從哪裏發生的請求行爲。
initiatorType
屬性會返回下面列表中列出的字符串中的其中一個:
類型 | 描述 |
---|---|
css | 若是請求是從CSS中的url() 指令發出的,例如 @import url() 或 background: url() |
xmlhttprequest | 經過XMLHttpRequest對象發出的請求 |
fetch | 經過Fetch方法發出的請求 |
beacon | 經過beacon方法發出的請求 |
link | 經過link標籤發出的請求 |
script | 經過script標籤發出的請求 |
iframe | 經過iframe標籤發出的請求 |
other | 沒有匹配上面條件的請求 |
domainLookupStart
準確的說,domainLookupStart
屬性會返回下列值中的其中一個:
domainLookupStart
的值與fetchStart
相同domainLookupStart
等於開始從域信息緩存中檢索域數據的時間domainLookupEnd
domainLookupEnd
屬性會返回下列值中的其中一個:
domainLookupStart
相同,若是使用了持久鏈接(persistent connection),或者從相關應用緩存(relevant application cache)或本地資源中獲取資源,那麼domainLookupEnd
的值與fetchStart
相同domainLookupEnd
爲從域信息緩存中檢索域數據結束時的時間下圖給出了PerformanceResourceTiming
對象定義的時序屬性。當從不一樣來源獲取資源時,括號中的屬性可能不可用。用戶代理能夠在時間點之間執行內部處理。
圖1 PerformanceResourceTiming 過程模型
精準地測量Web應用的性能是使Web應用更快的一個重要方面。雖然利用JavaScript提供的能力能夠測量用戶等待時間(咱們常說的埋點),但在更多狀況下,它並不能提供完整或詳細的等待時間。例如,下面的JavaScript使用了一個很是天真的方式嘗試測量頁面徹底加載完所須要的時間:
<html>
<head>
<script type="text/javascript"> const start = new Date().getTime() function onLoad() { const now = new Date().getTime() const latency = now - start console.log('page loading time: ' + latency) } </script>
</head>
<body onload="onLoad()">
<!- Main page body goes from here. -->
</body>
</html>
複製代碼
上面的代碼將計算在執行head
標籤中的第一行JavaScript以後加載頁面所需的時間,可是它沒有提供任何有關從服務端獲取頁面所需的時間信息,或頁面的初始化生命週期。
對於這種需求,W3C性能工做組定義了Navigation Timing規範,該規範定義了PerformanceNavigationTiming
接口,提供了更有用和更準確的頁面加載相關的時間數據。包括從網絡獲取文檔到在用戶代理(User Agent)中加載文檔相關的全部時間信息。
對於上面那個例子,使用Navigation Timing能夠很輕鬆的用下面的代碼作到而且更精準:
<html>
<head>
<script type="text/javascript"> function onLoad() { const [entry] = performance.getEntriesByType('navigation') console.log('page loading time: ' + entry.duration) } </script>
</head>
<body onload="onLoad()">
<!- Main page body goes from here. -->
</body>
</html>
複製代碼
上面代碼經過performance.getEntriesByType
方法獲得一個列表,這個列表就是咱們前面2.1節介紹的PerformanceEntryList
,並過濾出全部類型爲navigation
的PerformanceEntry
對象。
類型爲navigation
的PerformanceEntry
對象在術語上被稱爲PerformanceNavigationTiming
對象。
PerformanceNavigationTiming
對象擴展了PerformanceEntry
對象,經過該對象提供的duration
屬性能夠獲得頁面加載所消耗的所有時間。
PerformanceNavigationTiming 接口所提供的全部時間值都是相對於 Time Origin 的。因此 startTime 屬性的值永遠是0
經過該PerformanceNavigationTiming
對象能夠得到頁面加載相關的很是精準的時間信息:
0
loadEventEnd
的時間減去startTime
的時間)PerformanceNavigationTiming
對象擴展了PerformanceResourceTiming
對象,因此PerformanceNavigationTiming
對象具備PerformanceResourceTiming
對象的全部屬性,可是某些屬性的返回值略有不一樣:
同時 NavigationTiming 新增了一些屬性,下面列表給出了新增的屬性:
新增的屬性 | 描述 |
---|---|
unloadEventStart | 若是被請求的頁面來自於前一個同源(同源策略)的文檔,那麼該屬性存儲的值是瀏覽器開始卸載前一個文檔的時刻。不然的話(前一個文檔非同源或者沒有前一個文檔)爲0 |
unloadEventEnd | 前一個文檔卸載完成的時刻。若是前一個文檔不存在則爲0 |
domInteractive | 指文檔完成解析的時間,包括在「傳統模式」下被阻塞的經過script標籤加載的內容(使用defer或者async屬性異步加載的狀況除外) |
domContentLoadedEventStart | DOMContentLoaded事件觸發前的時間 |
domContentLoadedEventEnd | DOMContentLoaded事件觸發後的時間 |
domComplete | 用戶代理將將document.readyState 設置爲complete 的時間 |
loadEventStart | load事件被觸發前的時間,若是load事件還沒觸發則返回0 |
loadEventEnd | load事件完成後的時間,若是load事件還沒觸發則返回0 |
redirectCount | 頁面被重定向的次數 |
type | 頁面被載入的方式 |
type
屬性的四種取值狀況:
location.reload()
方法prerender
的方式啓動一個頁面圖2給出了PerformanceNavigationTiming
對象的時序屬性。當頁面從不一樣來源獲取時,括號中的屬性可能不可用。
圖2 PerformanceNavigationTiming 過程模型
從圖2能夠看出完整的頁面加載時間信息包含不少信息。前端渲染相關的時間只佔用不多的一部分(圖2最後面兩個藍色部分processing
與onLoad
)。這也是爲何咱們在一開始說使用JS埋點的方式去測量頁面加載時間很天真。
Web開發者須要一種可以**「評估與理解」**其Web應用性能的能力。雖然JavaScript提供了測量應用性能的能力(使用Date.now()
方法獲取當前時間戳),但這個時間戳的精度在不一樣的用戶代理下存在必定的差別,而且時間會受到系統時鐘誤差與調整的影響。
W3C性能工做組定義了User Timing規範,提供了高精度且單調遞增的時間戳,使開發者能夠更好地測量其應用的性能。
下面代碼顯示了開發者應該如何使用User Timing規範定義的API來得到執行代碼相關的時間信息。
async function run() {
performance.mark("startTask1")
await doTask1() // Some developer code
performance.mark("endTask1")
performance.mark("startTask2")
await doTask2() // Some developer code
performance.mark("endTask2")
// Log them out
const entries = performance.getEntriesByType("mark")
for (const entry of entries) {
console.table(entry.toJSON())
}
}
run()
複製代碼
User Timing規範擴展了Performance
對象,並在Performance
對象上新增了四個方法:
mark方法接收一個字符串類型的參數(mark名稱),用於建立並存儲一個PerformanceMark
對象。更通俗的說,mark方法用於記錄一個與名稱相關時間戳。
PerformanceMark
對象存儲了4個屬性:
performance.now()
方法的返回值)下面代碼展現瞭如何使用mark
方法:
performance.mark('testName')
複製代碼
當使用mark
方法存儲了一個PerformanceMark
對象後,能夠經過前面介紹的getEntriesByName
方法獲得一個列表,列表中包含一個PerformanceMark
對象。代碼以下:
const [entry] = performance.getEntriesByName('testName')
console.log(entry) // {"name": "testName", "entryType": "mark", "startTime": 4396.399999997811, "duration": 0}
複製代碼
顧名思義,clearMarks
方法的做用是刪除全部給定名稱的時間戳數據(PerformanceMark
對象)。
clearMarks
方法接收一個字符串類型的參數(mark名稱),例如:
performance.mark('testName')
performance.clearMarks('testName')
performance.getEntriesByName('testName') // []
複製代碼
上面代碼使用mark
方法記錄了一個名爲testName
的時間戳信息(存儲了PerformanceMark
對象),隨後使用clearMarks
方法清除名爲testName
的時間戳信息,最後嘗試獲取名爲testName
的時間戳信息時獲得的是一個空列表。
雖然mark
方法能夠記錄時間戳信息,可是得到兩個mark
之間的持續時間仍是有點麻煩,咱們須要先獲取兩個PerformanceMark
對象,而後再執行減法。
針對這個問題User Timing規範提供了measure
方法,該方法的做用是使用一個名字將兩個PerformanceMark
對象之間所持續的時間存儲起來。
measure
方法的參數:
與mark
方法相同,measure
方法會建立一個PerformanceMeasure
對象並存儲起來。PerformanceMeasure
對象存儲了4個屬性:
PerformanceMark
對象的startTime
屬性,若是沒有提供startMark
參數,則爲0PerformanceMark
對象的startTime
屬性的差值,多是負數。下面代碼展現瞭如何使用measure
方法檢測代碼執行所持續的時間:
async function run() {
performance.mark('startTask')
await doTask1() // Some developer code
performance.mark('endTask')
performance.measure('task', 'startTask', 'endTask')
// Log them out
const [entry] = performance.getEntriesByName('task')
console.log(entry.duration)
}
run()
複製代碼
與clearMarks
相似,clearMeasures
方法的做用是使用參數中提供的名稱來刪除PerformanceMeasure
對象。
保證UI的流暢很重要,那麼如何檢測UI是否流暢呢?
根據RAIL性能模型提供的信息,若是Web應用在100毫秒內的時間能夠響應用戶輸入,則用戶會以爲應用的交互很流暢。若是響應超過100毫秒用戶就會感受到應用有點輕微的延遲。若是超過1秒,用戶的注意力將離開他們正在執行的任務。
因爲JavaScript是單線程的,因此當一個任務執行時間過長,就會阻塞UI線程與其餘任務。對於用戶來講,他一般會看到一個「鎖定」的頁面,瀏覽器沒法響應用戶輸入。
這種佔用UI線程很長一段時間並阻止其餘關鍵任務執行的任務叫作「長任務」
更具體的解釋是:超過50毫秒的事件循環任務都屬於長任務
那麼如何檢測應用是否存在「長任務」呢?
一個已知的方式是使用一個短週期定時器,並檢查兩次調用之間的時間,若是兩次調用之間的時間大於定時器的週期時間,那麼頗有可能有一個或多個「長任務」延遲了定時器的執行。
這種方式雖然能夠實現需求,但它並不完美。它要不停的輪詢去檢查長任務,在移動端對手機電池壽命不友好,而且也沒有辦法知道是誰形成了延遲(例如:本身的代碼 vs 第三方的代碼)。
W3C性能工做組提供了Long Tasks規範,該規範定義了一個接口,使Web開發者能夠監測「長任務」是否存在。
使用案例:
const observer = new PerformanceObserver(function(list) {
const perfEntries = list.getEntries()
for (let i = 0; i < perfEntries.length; i++) {
// 處理長任務通知
// 上報性能檢測數據
// ...
}
})
// 註冊長任務觀察者
observer.observe({entryTypes: ['longtask']})
// 模擬一個長任務
const start = Date.now()
while (Date.now() - start < 1000) {}
複製代碼
上面的代碼註冊了「長任務」觀察器,它的功能是每當有超過50毫秒的任務被執行時調用回調函數。
2.3節介紹了PerformanceObserver
,因此回調函數中的變量perfEntries
保存了一個列表,列表中包含了全部承載了長任務數據的對象。
承載了長任務數據的對象在術語上被稱爲PerformanceLongTaskTiming
。
PerformanceLongTaskTiming
對象中保存了長任務相關的信息,包括如下屬性:
same-origin-ancestor
)same-origin-ancestor
相反,若是當前頁面註冊了一個長任務觀察者並iframe了一個其餘頁面,這時候iframe中若是存在長任務,則當前頁面的長任務觀察者會收到通知,這時候name屬性的值爲same-origin-descendant
)name
屬性爲multiple-contexts
)Time Origin
的時間TaskAttributionTiming
對象,該對象有如下屬性:
frame指的是瀏覽上下文,例如iframe
加載並非一個單一的時刻,它是一種體驗,沒有任何一種指標能夠徹底捕獲。事實上在頁面加載期間有多個時刻能夠影響用戶將其視爲「快」仍是「慢」。
首次繪製(FP,全稱First Paint)是第一個比較關鍵的時刻,其次是首次內容繪製(FCP,全稱First Contentful Paint)。
這兩個性能指標之間的主要區別在於「首次繪製」是當瀏覽器首次開始渲染任何能夠在視覺上讓屏幕發生變化的時刻。相比之下「首次內容繪製」是當瀏覽器首次從DOM中渲染內容的時刻,內容能夠是文本,圖片,SVG,甚至是canvas元素。
圖3 首屏渲染指標」首次繪製「(First Paint)不包括默認背景繪製(例如瀏覽器默認的白色背景),可是包含非默認的背景繪製,與iframe。
」首次內容繪製「(First Contentful Paint)包含文本,圖片(包含背景圖),非白色canvas與SVG。
父級瀏覽上下文不該該知道子瀏覽上下文的繪製事件,反之亦然。這就意味着若是一個瀏覽上下文只包含一個iframe,那麼將只有「首次繪製」,但沒有「首次內容繪製」。
能夠經過下面代碼得到首屏渲染性能指標數據:
performance.getEntriesByType('paint')
複製代碼
經過上面這行代碼能夠獲得一個列表。列表中包含一個或兩個PerformancePaintTiming
對象。這取決於「首次內容繪製」是否存在。如圖4所示:
圖4. 獲取首屏渲染指標
從圖3能夠看到PerformancePaintTiming
對象包含四個屬性,這四個屬性的值爲:
time origin
的咱們可使用下面的代碼註冊一個繪製觀察器:
const observer = new PerformanceObserver(function(list) {
const perfEntries = list.getEntries()
for (let i = 0; i < perfEntries.length; i++) {
// 處理數據
// 上報性能檢測數據
// ...
}
})
// 註冊繪製觀察者
observer.observe({entryTypes: ["paint"]})
複製代碼
本文詳細介紹了在Web應用中採集性能信息所須要的一些方法。其中包括:得到不受時鐘誤差與系統時鐘調整影響的高精度時間的方法、收集「頁面資源加載」相關的性能度量數據的方法、收集「網頁加載」相關的性能度量數據的方法、使用高精度時間戳在應用程序中埋點的方法、監測用戶以爲網頁「慢」的方法以及採集首屏渲染性能指標的方法。