User Time API

瞭解網頁應用程式

Alex Danilo

高效能的網頁應用程式是提供優質使用者體驗的關鍵。隨著網頁應用程式越來越複雜,瞭解效能影響對於打造吸引人的體驗至關重要。過去幾年來,瀏覽器中出現了許多不同的 API,可協助分析網路效能、載入時間等,但這些 API 不一定能提供精細的詳細資料,且彈性不足,無法找出應用程式速度變慢的原因。User Timing API 提供的機制可檢測網頁應用程式,找出應用程式耗費時間的地方。本文將介紹這個 API,並舉例說明使用方式。

無法衡量,就無從最佳化

如要加快網頁應用程式的速度,第一步是找出時間花在哪裡。如要找出熱點,最理想的做法是評估 JavaScript 程式碼區域的時間影響,這是找出如何提升效能的第一步。幸好,User Timing API 可讓您在 JavaScript 的不同部分插入 API 呼叫,然後擷取詳細的使用者載入時間資料,協助您進行最佳化。

高解析度時間和now()

精確度是準確測量時間的基本要素。過去,我們以毫秒為單位測量時間,這沒什麼問題,但如果要建構無卡頓的 60 FPS 網站,每個影格都必須在 16 毫秒內繪製完成。因此,如果精確度只有毫秒,就無法提供準確分析所需的精確度。高解析度時間是新式瀏覽器內建的新計時類型。高解析度時間提供浮點時間戳記,精確度可達微秒解析度,比以往高出一千倍。

如要在網頁應用程式中取得目前時間,請呼叫 now() 方法,該方法會形成 Performance 介面的擴充功能。以下程式碼說明如何執行這項操作:

var myTime = window.performance.now();

另一個介面稱為 PerformanceTiming,可提供與網頁應用程式載入方式相關的多個不同時間。now() 方法會傳回自 PerformanceTiming 中的 navigationStart 時間發生後經過的時間。

DOMHighResTimeStamp 類型

過去嘗試為網頁應用程式計時,您會使用 Date.now() 等方法,傳回 DOMTimeStamp。DOMTimeStamp 會傳回整數毫秒數做為值。為了提供高解析度時間所需的更高準確度,我們推出了名為 DOMHighResTimeStamp 的新類型。這個類型是浮點值,也會傳回以毫秒為單位的時間。但由於是浮點數,因此值可以代表毫秒分數,因此可以產生千分之一毫秒的準確度。

使用者載入時間介面

現在我們有了高解析度時間戳記,接著使用 User Timing 介面擷取時間資訊。

使用者載入時間介面提供多種函式,可讓我們在應用程式的不同位置呼叫方法,提供類似 Hansel 和 Gretel 的導覽標記路徑,追蹤時間花費的位置。

使用mark()

mark() 方法是時間分析工具包中的主要工具。mark() 的作用是為我們儲存時間戳記。mark() 的實用之處在於,我們可以為時間戳記命名,API 會將名稱和時間戳記視為一個單位。

在應用程式中的不同位置呼叫 mark(),即可計算出網頁應用程式達到該「標記」所需的時間。

規格中列出多個建議的標記名稱,這些名稱可能很有趣,而且相當淺顯易懂,例如 mark_fully_loaded、mark_fully_visible、mark_above_the_fold 等。

舉例來說,我們可以使用下列程式碼,在應用程式完全載入時設定標記:

window.performance.mark('mark_fully_loaded');

在網頁應用程式中設定命名標記,就能收集大量時間資料,並在空閒時分析,瞭解應用程式的運作內容和時間。

使用 measure() 計算測量結果

設定多個時間標記後,你可能會想知道標記之間經過的時間。請使用 measure() 方法執行這項操作。

measure() 方法會計算標記之間的經過時間,也可以測量標記與 PerformanceTiming 介面中任何已知事件名稱之間的時間。

舉例來說,您可以使用類似下列的程式碼,計算從 DOM 完成到應用程式狀態完全載入的時間:

window.performance.measure('measure_load_from_dom', 'domComplete', 'mark_fully_loaded');

呼叫 measure() 時,系統會儲存結果,不受設定的標記影響,方便日後擷取。在應用程式執行時儲存時間,應用程式就能保持回應,您可以在應用程式完成部分工作後,將所有資料傾印出來,以便日後分析。

捨棄含有 clearMarks() 的標記

有時,能夠清除設定的一堆標記會很有用。舉例來說,您可能想在網頁應用程式上執行批次作業,因此希望每次執行時都重新開始。

只要呼叫 clearMarks(),就能輕鬆移除設定的任何標記。

因此,下方的程式碼範例會清除所有現有標記,方便您視需要再次設定計時執行作業。

window.performance.clearMarks();

當然,在某些情況下,您可能不想清除所有標記。因此,如要移除特定標記,只要傳遞要移除的標記名稱即可。舉例來說,以下程式碼:

window.performance.clearMarks('mark_fully_loaded');

會移除我們在第一個範例中設定的標記,同時保留我們設定的其他標記。

您可能也想移除已建立的任何測量,而 clearMeasures() 方法可以做到這一點。運作方式與 clearMarks() 完全相同,但會套用至你進行的任何測量。例如,以下程式碼:

window.performance.clearMeasures('measure_load_from_dom');

會移除我們在上述 measure() 範例中建立的測量。如要移除所有測量指標,做法與 clearMarks() 相同,只要呼叫不含引數的 clearMeasures() 即可。

取得時間資料

設定標記和測量間隔固然很好,但有時您會想取得時間資料來進行分析。這也相當簡單,只要使用 PerformanceTimeline 介面即可。

舉例來說,getEntriesByType() 方法可讓我們取得所有標記時間,或以清單形式取得所有測量時間,以便疊代並消化資料。這份清單會依時間順序傳回,因此您可以按照在網頁應用程式中觸發的順序查看標記。

以下程式碼:

var items = window.performance.getEntriesByType('mark');

會傳回網頁應用程式中所有已觸發的標記清單,而以下程式碼:

var items = window.performance.getEntriesByType('measure');

會傳回所有測量結果的清單。

您也可以使用特定名稱,取回項目清單。舉例來說,以下程式碼:

var items = window.performance.getEntriesByName('mark_fully_loaded');

會傳回一個清單,其中包含一個項目,該項目在 startTime 屬性中包含「mark_fully_loaded」時間戳記。

XHR 要求計時 (範例)

現在我們對 User Timing API 有了初步瞭解,可以運用這項工具分析網頁應用程式中所有 XMLHttpRequests 的耗時。

首先,我們會修改所有 send() 要求,發出設定標記的函式呼叫,同時使用設定另一個標記的函式呼叫來變更成功回呼,然後產生要求所需時間的測量結果。

因此,XMLHttpRequest 通常會像這樣:

var myReq = new XMLHttpRequest();
myReq.open('GET', url, true);
myReq.onload = function(e) {
  do_something(e.responseText);
}
myReq.send();

在本範例中,我們會新增全域計數器來追蹤要求數量,並用來儲存每項要求的測量結果。程式碼如下所示:

var reqCnt = 0;

var myReq = new XMLHttpRequest();
myReq.open('GET', url, true);
myReq.onload = function(e) {
  window.performance.mark('mark_end_xhr');
  reqCnt++;
  window.performance.measure('measure_xhr_' + reqCnt, 'mark_start_xhr', 'mark_end_xhr');
  do_something(e.responseText);
}
window.performance.mark('mark_start_xhr');
myReq.send();

上述程式碼會為我們傳送的每個 XMLHttpRequest 產生一個具有不重複名稱值的測量。我們假設要求是依序執行,平行要求的程式碼需要稍微複雜一點,才能處理不按順序傳回的要求,這部分就留給讀者練習。

網頁應用程式完成一系列要求後,我們可以使用下列程式碼將所有要求傾印到控制台:

var items = window.performance.getEntriesByType('measure');
for (var i = 0; i < items.length; ++i) {
  var req = items[i];
  console.log('XHR ' + req.name + ' took ' + req.duration + 'ms');
}

結論

User Timing API 提供許多實用工具,可套用至網頁應用程式的任何層面。只要在網頁應用程式中加入 API 呼叫,並對產生的時間資料進行後續處理,就能輕鬆縮小應用程式中的熱點範圍,清楚瞭解時間花費在何處。但如果瀏覽器不支援這個 API,該怎麼辦?沒問題,這裡有很棒的 Polyfill,可以完美模擬 API,並與 webpagetest.org 搭配使用。事不宜遲,立即在應用程式中試用 User Timing API,找出加快載入速度的方法,為使用者提供更優質的體驗。