若是咱們想要提升咱們的前端性能,那麼首先你得先只知道怎麼測量你的前端性能。可是哪些前端的性能指標咱們應該關注呢? 在Web1.0時代當咱們討論到前端性能指標用的最多的就是page load time
。到了單頁應用程序愈來愈多的時代,單一的page load time
指標已經沒法徹底衡量前端性能了,由於一個單頁應用程序page load time
可能只觸發了一次,可是用戶會跟程序有不少的交互,每個交互或者動做都須要有指標來衡量他的性能。javascript
接下來就介紹幾個比較重要的前端性能指標:前端
這兩個指標對用戶來講都是很重要的,這兩個指標能夠認爲咱們程序正在告訴咱們的用戶:咱們正在正確工做。java
這也是很是關鍵的一個指標,一般若是用戶可以快速的看到最重要的一塊區域被渲染完成,即便其餘的區域都尚未被渲染用戶可能也不會注意到。git
指標 | 介紹 |
---|---|
first paint (FP)/first contentful paint (FCP) | 程序是否正確的開始渲染 |
First meaningful paint (FMP) | 用戶最關注的的首屏內容顯示 |
Time to interactive (TTI) | 程序是否可用 |
long task | 程序使用的體驗(是否響應延遲,動畫卡頓) |
還有一些其餘的指標好比 First Input Delay(首次接受用戶響應的延遲時間) First CPU Idle(第一次CPU閒置的時間):這些指標都和上面的指標有着直接的關係。github
知道了這些指標,咱們要把這些指標控制在什麼樣的時間才能給用戶帶來比較好的用戶的體驗呢,下面有這樣一張表web
時間 | 介紹 |
---|---|
0到16ms | 用戶但願看的動畫可以流暢,動畫卡頓會帶來很是差的用戶體驗,在瀏覽器上每秒鐘渲染60幀動畫就可以保持流暢,這大約就是16ms渲染一幀,這16ms包括了瀏覽器要渲染新的元素到頁面上須要的時間,也就是說程序有大約10ms的時間能夠進行操做。 |
0到100ms | 在這個時間內響應用戶的交互,用戶會以爲響應是很是及時的 |
100到300ms | 用戶會感受到有一些延遲 |
300到1000ms | 當執行一些頁面加載或者頁面跳轉的時候,在這個時間內是一個正常的加載跳轉時間 |
1000ms或以上 | 超過1000ms(1秒),用戶會對以前的操做漸漸失去耐心和注意力 |
10000ms或以上 | 當你的響應超過10秒,用戶會感到煩躁,而後終止以前操做 |
上面的延遲時間取決於你使用什麼樣的網絡和設備,好比你使用的電腦和wifi網絡,用戶在1000ms是一個比較現實的目標。可是當你設備是手機網絡只有3G的時候在5000ms內加載纔是更現實的目標。canvas
在大多數的時間裏,用戶在使用程序時大多數的時間都在等待網站響應他們的操做好比點擊一個按鈕,在文本框中輸入內容,而不是等待網站加載。那麼網站比較理想的響應用戶時間是在100ms之內。瀏覽器
咱們的目標是在100ms之內響應用戶的操做,那爲何處理用戶的響應時間只有50ms?由於在咱們接受到用戶的輸入時,可能會有別的任務正在執行.好比咱們接受到用戶在文本框中輸入了一個A,這個時候瀏覽器正在執行別的任務,瀏覽器會把這個操做先加入到任務隊列裏,等瀏覽器執行完以前的任務纔會去處理用戶的響應。那麼保守的估計爲了讓用戶在100ms之內得到響應,咱們的處理用戶響應的執行時間就是50ms。 性能優化
理論上來講只要沒16ms渲染一幀,動畫就會看起來是流暢的,可是瀏覽器大約須要6ms的時間來將每一幀渲染到畫面上.所以產生每一幀動畫的時間留給程序的大約就10ms左右。服務器
主線程可以有儘量多的閒置時間,那麼當產生用戶交互時就能夠立馬給用戶響應。當主線程閒置時,瀏覽器會有不少的內部程序須要執行,好比閒置GC等。
當頁面加載緩慢,用戶會失去耐心。網站的加載和響應速度直接影響用戶的體驗。
在介紹如何在用戶真實環境中或者這些指標以前先介紹一些API
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
//這裏的name是爲了區分'first-paint','first-contentful-paint'
const metricName = entry.name;
const time = Math.round(entry.startTime + entry.duration);
reportToServer({
eventCategory: 'Performance Metrics',
eventAction: metricName,
eventValue: time,
nonInteraction: true,
});
}
});
observer.observe({entryTypes: ['paint']});
複製代碼
以前已經介紹過對於瀏覽器來講很那準確的知道每一個網站對重要的一塊區域顯示的時間,那麼只有開發者本身可以準確的找到最重要的一塊區域得到FMP,假設咱們網站首屏最重要的元素是一個圖片就能夠這麼寫.
<img src="important.jpg" onload="performance.clearMarks('img displayed'); performance.mark('img displayed');">
<script> performance.clearMarks("img displayed"); performance.mark("img displayed"); </script>
複製代碼
目前在PerformanceObserver中尚未辦法得到TTI的接口,經過這個tti-polyfill能夠知道這個tti
import ttiPolyfill from 'tti-polyfill.js';
ttiPolyfill.getFirstConsistentlyInteractive().then((tti) => {
reportToServer({
eventCategory: 'Performance Metrics',
eventAction: 'TTI',
eventValue: tti,
nonInteraction: true,
});
});
複製代碼
以前提到過,長任務可能會影響瀏覽器對用戶響應速度或者形成動畫的卡頓.那麼能意識到長任務的存在而且把他縮短是頗有必要的。(長任務API認爲50ms以上任務的爲長任務)
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
reportToServer({
eventCategory: 'Performance Metrics',
eventAction: 'longtask',
eventValue: Math.round(entry.startTime + entry.duration),
//這裏的長任務會包含一個attribute
//https://w3c.github.io/longtasks/#sec-TaskAttributionTiming
eventLabel: JSON.stringify(entry.attribution),
});
}
});
observer.observe({entryTypes: ['longtask']});
複製代碼
長任務會阻塞線程致使瀏覽器沒法響應用戶操做,以前也提到過若是能在100ms之內響應用戶的操做就不會讓用戶以爲卡頓,那麼若是可以監控到你關鍵交互的響應時間也是頗有必要的
const submitBtn = document.querySelector('#submit');
submitBtn.addEventListener('click', (event) => {
const lag = performance.now() - event.timeStamp;
if (lag > 100) {
reportToServer({
eventCategory: 'Performance Metric'
eventAction: 'input-latency',
eventLabel: '#subscribe:click',
eventValue: Math.round(lag),
nonInteraction: true,
});
}
});
複製代碼
當咱們的程序若是加載速度很慢(好比加載了過多的js),那麼真實用戶在網絡環境不一致的狀況下,有些響應過慢的用戶可能早早的在加載完成前就已經退出網站,那麼這裏就會有一個倖存者誤差的問題.你監控的用戶都是已經加載完成的用戶。爲了可以同時檢測到退出的用戶.
///寫在最頭部
window.__trackAbandons = () => {
// 去掉事件監聽那麼這個方法只執行一次
document.removeEventListener('visibilitychange', window.__trackAbandons);
//由於咱們尚未加載report js API 因此咱們要讓服務器提供一個post接口在接收此次請求
const ANALYTICS_URL = 'https://ANALYTICS_URL';
const TRACKING_ID = 'TRACKING_ID';
const CLIENT_ID = (Math.random() * Math.pow(2, 52));
// Send the data to Google Analytics via the Measurement Protocol.
navigator.sendBeacon && navigator.sendBeacon(ANALYTICS_URL, [
'v=1', 't=event', 'ec=Load', 'ea=abandon', 'ni=1',
'dl=' + encodeURIComponent(location.href),
'dt=' + encodeURIComponent(document.title),
'tid=' + TRACKING_ID,
'cid=' + CLIENT_ID,
'ev=' + Math.round(performance.now()),
].join('&'));
};
//visibilitychange能夠監聽頁面unload事件
//https://developer.mozilla.org/en-US/docs/Web/API/Document/visibilitychange_event
document.addEventListener('visibilitychange', window.__trackAbandons);
複製代碼
性能對如今的程序愈來愈重要,那麼在一個程序須要進行性能優化的時候.我的認爲能夠按照這樣的一個順序進行:
數據收集->討論性能指標閥值->針對性能優化->數據從新驗證優化結果
這裏主要討論了咱們一些性能指標和收集性能指標的方法,後面會討論如何針對每一塊進行優化。