前端頁面效能指標與採集方式

Young Dreamer發表於2019-07-09

主要用於測試html的頁面效能採集。介紹了傳統的效能指標和採集方式,
此外介紹了Google正在力推的以使用者為中心的效能指標,以及相應的採集方式。

效能指標

目前業界常用的指標就是:白屏首屏domreadypageloaded四個指標,在usual-index.html中,
我們通過performance API獲取到響應的指標值。

  • 白屏

一般是認為DOM Tree構建時,解析到或

的時候,我們認為是白屏結束的時間點。
我們可以在這個時候使用performace.mark進行打點標記,最後可以通過performance的
entry.startTime來獲取白屏時間,其中entry.startTime是相對於performance.timing.navigationStart的時間。
<head>
...
<script>
    // 通常在head標籤尾部時,打個標記,這個通常會視為白屏時間
    performance.mark("first paint time");
</script>
</head>
<body>
...
<script>
    // get the first paint time
    const fp = Math.ceil(performance.getEntriesByName('first paint time')[0].startTime);
</script>
</body>
  • 首屏

一般是首屏中的圖片載入完畢的時候,我們認為是首屏結束的時間點。我們可以對首屏中的image做onload事件繫結,
performace.mark進行打點標記,不過打點前先進行performance.clearMarks清除操作,以獲取到多張圖片最後載入完畢的時間。

<body>
<div class="app-container">
    <img src="a.png" onload="heroImageLoaded()">
    <img src="b.png" onload="heroImageLoaded()">
    <img src="c.png" onload="heroImageLoaded()">
</div>
<script>
    // 根據首屏中的核心元素確定首屏時間
    performance.clearMarks("hero img displayed");
    performance.mark("hero img displayed");
    function heroImageLoaded() {
        performance.clearMarks("hero img displayed");
        performance.mark("hero img displayed");
    }
</script>
...
...
<script>
    // get the first screen loaded time
    const fmp = Math.ceil(performance.getEntriesByName('hero img displayed')[0].startTime);
</script>
</body>
  • domready與pageloaded

這兩個指標有相應的事件監聽,即document的DOMContentLoaded和window.onload,直接在事件的回撥中使用performance打點即可。

<script>
    document.addEventListener('DOMContentLoaded', ()=> {
        performance.mark("dom ready");
    });
    window.onload = ()=> {
        performance.mark("page loaded");
        // get the domReady time
        const domReady = Math.ceil(performance.getEntriesByName('dom ready')[0].startTime);
        // get the page loaded time
        const pageLoad = Math.ceil(performance.getEntriesByName('page loaded')[0].startTime);
    }
</script>

以使用者為中心的效能指標

這個是Google力推的指標,主要從4個視覺反饋階段來描述頁面效能。

視覺反饋 頁面狀態 效能指標
是否發生? 導航是否成功啟動?伺服器是否有響應? 首次繪製 (FP)/首次內容繪製 (FCP)
是否有用? 是否已渲染可以與使用者互動的足夠內容? 首次有效繪製 (FMP)/主角元素計時
是否可用? 使用者可以與頁面互動,還是頁面仍在忙於載入? 可互動時間 (TTI)
是否令人愉快? 互動是否順暢而自然,沒有滯後和卡頓? 耗時較長的任務(在技術上不存在耗時較長的任務)

對應的指標如下圖所示:
image
此外,Google也提供了一些新的API,來獲取相應的指標值。

  • 首次繪製 (FP)/首次內容繪製 (FCP)

PerformanceObserver 為我們提供的新功能是,能夠在效能事件發生時訂閱這些事件,並以非同步方式響應事件。

let perfomanceMetrics = {};
const observer = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
        // `entry` is a PerformanceEntry instance.
        // `name` will be either 'first-paint' or 'first-contentful-paint'.
        const metricName = entry.name;
        const time = Math.round(entry.startTime + entry.duration);
        if (metricName === 'first-paint') {
            perfomanceMetrics.fp = time;
        }
        if (metricName === 'first-contentful-paint') {
            perfomanceMetrics.fcp = time;
        }
    }
});

// Start observing the entry types you care about.
observer.observe({entryTypes: ['paint']});
  • 首次有效繪製 (FMP)/主角元素計時

目前尚無標準化的 FMP 定義,一般來說,是將 FMP 視為主角元素呈現在螢幕上的時刻。
這個的計算方法就同上面介紹的首屏指標獲取,只是首屏確定的是首頁中的圖片,而 FMP 確定的是核心元素。

  • 可互動時間 (TTI)

TTI 主要是通過跟蹤耗時較長的任務來確定,設定PerformanceObserver觀察型別為 longtask 的條目,
然後可以根據耗時較長的條目的startTime和duration,來大致確認頁面處於idle的時間,從而確定 TTI 指標。
Google希望將 TTI 指標標準化,並通過 PerformanceObserver 在瀏覽器中公開,但目前並不支援。
目前只能通過一個 polyfill,檢測目前的 TTI,適用於所有支援 Long Tasks API 的瀏覽器。
該 polyfill 公開 getFirstConsistentlyInteractive() 方法,後者返回使用 TTI 值進行解析的 promise。
用法如下所示:

  1. 首先是在中設定PerformanceObserver,並指定監控型別為longtask。
<head>
<script>
    // collect the longtask
    if (PerformanceLongTaskTiming) {
        window.__tti = {e: []};
        const observer = new PerformanceObserver((list) => {
            for (const entry of list.getEntries()) {
                // observe the longtask to get the time to interactive (TTI)
                if (entry.entryType === 'longtask') {
                    window.__tti.e.concat(entry);
                }
            }
        });
        observer.observe({entryTypes: ['longtask']});
    }
</script>
</head>
  1. 然後引入tti-polyfill.js(可通過npm包獲取),獲取到tti的值。
import ttiPolyfill from './path/to/tti-polyfill.js';

ttiPolyfill.getFirstConsistentlyInteractive().then((tti) => {
  ...
});
  • 耗時較長的任務

這個同TTI的第一步,設定PerformanceObserver,並指定監控型別為longtask,
獲取到的entry包含提供方屬性,有助於追查導致出現耗時較長任務的程式碼。

示例demo

https://github.com/huangwenming/learning-notes/tree/master/html-relevant/performance

參考資料

http://www.alloyteam.com/2015/09/explore-performance/

https://speedcurve.com/blog/user-timing-and-custom-metrics/

https://developers.google.com/web/fundamentals/performance/user-centric-performance-metrics#user-centric_performance_metrics

https://github.com/GoogleChromeLabs/tti-polyfill

相關文章