你的頁面爲何慢,Performance Timeline 簡介

介紹

Performance timeline
w3c 有兩個版本的規範,本文基於 第二版本 介紹。
工欲善其事,必先利其器。要想使頁面更快,那麼準確測量出性能數據也是很重要的。 咱們來看一下,在一個web頁面的生命週期以內,咱們基於 Performance Timeline能夠獲得哪些性能指標。javascript

主要分爲如下三大類:java

  • navigation-timing:navigation of the document
  • resource-timing:頁面資源
  • user-timing:開發者自定義的一些監控, 主要是(mark 和 measure,下文會講)

舉個🌰, w3c 上的例子,我稍加改造 :web

<img id="image0" src="https://www.w3.org/Icons/w3c_main.png" />
<script> function init() { // see [[USER-TIMING-2]] performance.mark("startWork"); setTimeout(() => { performance.mark("endWork"); measurePerf(); }, 2000); } function measurePerf() { performance .getEntries() .map(entry => JSON.stringify(entry, null, 2)) .forEach(json => console.log(json)); } 複製代碼

引入了一個 外部圖片,
mark 能夠理解爲標記了一個時間點
getEntries 獲得全部的性能數據,最後輸出。
具體結果可看:chrome

{
  "name": "",
  "entryType": "navigation",
  "startTime": 0,
  "duration": 50.07500003557652,
}
 {
  "name": "https://www.w3.org/Icons/w3c_main.png",
  "entryType": "resource",
}
 {
  "name": "startWork",
  "entryType": "mark",
  "startTime": 49.990000028628856,
  "duration": 0
}
 {
  "name": "first-paint",
  "entryType": "paint",
  "startTime": 94.83499999623746,
  "duration": 0
}
 {
  "name": "first-contentful-paint",
  "entryType": "paint",
  "startTime": 94.83499999623746,
  "duration": 0
}
 {
  "name": "endWork",
  "entryType": "mark",
  "startTime": 2050.5150000099093,
  "duration": 0
}
複製代碼

由此我就獲得了頁面上,當時的全部性能指標,包括navigation, resource, FP, FCP...等一些自定義的指標。json

ps:瀏覽器

  • FP:頁面上第一個像素落點的時候
  • FCP: 頁面上開始有內容繪製的時候

如今我想要過濾一下只要我本身 mark 的點,
可採用:getEntriesByType(mark)
只想要頁面繪製相關的 fp,fcp,
採用 getEntriesByName('first-paint')bash

but,咱們獲得的就只是當時獲得的性能指標,假如後面又有 圖片請求了呢,又有 js 請求了呢 🤣🤣。 咱們要一直 輪詢咱們的 measurePerf 嗎? 固然有新的解決方式。微信

PerformanceObserver

PerformanceObserver,是瀏覽器內部對Performance實現的觀察者模式,即: 當有性能數據產生時,主動通知你。dom

這解決了咱們以前的問題:async

  • 重複輪訓
  • 輪巡時不斷判斷,這個數據是新產生的,仍是之前的
  • 可能其餘數據的消費者也須要操做數據

監測頁面FP,FCP

如今能夠:

// 定義一個觀察者
const observer = new PerformanceObserver(list => {
    list.getEntries().forEach((entry) => {
        console.log('entry對象', entry);
    });
});
// 觀察的類型
observer.observe({
    entryTypes: ['paint']
});
複製代碼

關於 entryTypes, 能夠取以下值:

  • frame:event-loop 時的每一幀
  • navigation:導航
  • resource:資源
  • mark: 打點,獲得一個時間戳
  • measure:在兩個點之間測量
  • paint:繪製
  • longtask(好像只有 chrome支持):任何在瀏覽器中執行超過 50 ms 的任務,都是 long task

關於 entry: 每一個事件類型的 entry 對象都不同。

navigation entry 對象裏能拿到相關的數據有

這裏完整地描述了一個 頁面 呈現的完整流程。 拿到每一個時間點能夠進行分析每一個區間的時間耗費。

let t = entry
console.log('DNS查詢耗時 :' + (t.domainLookupEnd - t.domainLookupStart).toFixed(0))
console.log('TCP連接耗時 :' + (t.connectEnd - t.connectStart).toFixed(0))
console.log('request請求耗時 :' + (t.responseEnd - t.responseStart).toFixed(0))
console.log('解析dom樹耗時 :' + (t.domComplete - t.domInteractive).toFixed(0))
console.log('白屏時間 :' + (t.responseStart - t.navigationStart).toFixed(0))
console.log('domready時間 :' + (t.domContentLoadedEventEnd - t.navigationStart).toFixed(0))
console.log('onload時間 :' + (t.loadEventEnd - t.navigationStart).toFixed(0))
複製代碼

resource entry 對象裏能拿到相關的數據有

image

這裏道理和上面的 navigation 同樣。

mark

這裏打了四個點,用來標識兩個任務的開始時間和結束時間,兩個任務分別採用 Promise 推遲執行。doTask 模擬推遲行爲。

const doTask = (ms) => new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve()
  }, ms);
})
async function run() {
  performance.mark("startTask1");
  await doTask(1000); // Some developer code
  performance.mark("endTask1");

  performance.mark("startTask2");
  await doTask(2000); // Some developer code
  performance.mark("endTask2");

  // Log them out
  const entries = performance.getEntriesByType("mark");
  for (const entry of entries) {
    console.table(entry.toJSON());
  }
}
run();
複製代碼

分別獲得四個點的時間。

image

measure

用於在兩個 **點 (即上文提到的打點) ** 之間測量時間 舉個 例子:須要測量一個 for 循環花費的時間。

const observer = new PerformanceObserver(list => {
  list.getEntries().forEach((entry) => {
    console.table(entry);
  });
});

observer.observe({
  entryTypes: ['measure']
});
performance.mark('cal-start');
// 模擬耗時任務
for(let i = 0; i < 10000; i ++) {

}
performance.mark('cal-end');
// 該 measure 的名字:my-cal
// measure 開始的時間點:cal-start
// measure 結束的時間點:cal-end
performance.measure('my-cal', 'cal-start', 'cal-end')
複製代碼

image

longtask

這個 支持度 好像不高,chrome 親測能夠。 能夠檢測 應用裏 任何在瀏覽器中執行超過 50 ms 的任務。

緣由來源還比較多:

  • 瀏覽器的render
  • 自身的js執行

好比上面得 實例, 咱們僅需加入一個須要的檢測 的 entryType 既可。

observer.observe({
  entryTypes: ['measure', 'longtask']
});
複製代碼

能夠看到,Performance timeline 第二版本,相對之前仍是有很大改進的,咱們也有了更多的手段獲得想要監控的數據。 EOF。

參考

w3c
mdn-Long_Tasks_API
mdn-Performance-measure

最後

若是喜歡本篇文章,能夠關注的微信公衆號,若是不嫌煩,還能夠把它添加到桌面😀。

search.png

相關文章
相關標籤/搜索