User Time API

瞭解網頁應用程式

Alex Danilo

高效能的網頁應用程式是提供良好使用者體驗的關鍵。隨著網頁應用程式日益複雜,瞭解效能影響是打造引人入勝體驗的關鍵。過去幾年,瀏覽器中出現了許多不同的 API,可協助分析網路效能、載入時間等,但這些 API 不一定能提供足夠彈性的細節資訊,讓您找出應用程式運作緩慢的原因。請輸入 User Timing API,這個 API 提供一種機制,可用於檢測網路應用程式耗費時間的所在位置。本文將介紹該 API 以及使用方式的範例。

無法評估的項目無法最佳化

要加快網頁應用程式的速度,第一步就是找出耗費時間的地方。評估 JavaScript 程式碼區域的時間影響是找出熱點的最佳方法,也是找出如何改善效能的首要步驟。幸好,User Timing API 提供了一種方法,可在 JavaScript 的不同部分插入 API 呼叫,然後擷取詳細的時間資料,以利進行最佳化。

高解析度時間和 now()

精確度是準確測量時間的基礎。過去,我們會以毫秒為單位測量時間,這沒什麼問題,但如果要打造 60 FPS 的無頓挫網站,就必須在 16 毫秒內繪製每個影格。因此,如果精確度只有毫秒,就無法進行良好分析所需的精確度。輸入 高解析度時間,這是新式瀏覽器內建的新計時類型。高解析度時間可提供浮點時間戳記,精確度可達微秒,比先前快上千倍。

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

var myTime = window.performance.now();

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

DOMHighResTimeStamp 類型

在過去,如果您想測量網頁應用程式的時間,會使用 Date.now() 之類的函式,該函式會傳回 DOMTimeStamp。DOMTimeStamp 會傳回整數毫秒值做為其值。為了提供高解析度時間所需的更高準確度,我們推出了名為 DOMHighResTimeStamp 的新類型。這個類型是浮點值,也會以毫秒為單位傳回時間。但由於這是浮點值,因此可以代表小數毫秒,因此精確度可達到千分之一毫秒。

使用者時間介面

既然我們已取得高解析度的時間戳記,就讓我們使用「User Timing」介面,提取時間資訊。

User Timing 介面提供的函式可讓我們在應用程式的不同位置呼叫方法,以便提供 Hansel 和 Gretel 風格的麵包屑軌跡,讓我們追蹤時間花費的位置。

使用mark()

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

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

規格說明書會列出一些建議的標記名稱,這些名稱可能會引起使用者興趣,且相當直觀,例如 mark_fully_loadedmark_fully_visiblemark_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 有相當的瞭解,因此可以使用該 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,這個 polyfill 能非常精準地模擬 API,並與 webpagetest.org 搭配使用。事不宜遲,立即在應用程式中試用 User Timing API,瞭解如何加快應用程式速度,讓使用者體驗更上一層樓。