提升回應速度

瞭解我們對於評估回應速度的看法並提供意見回饋。

Annie Sullivan
Annie Sullivan
Hongbo Song
Hongbo Song
Nicolás Peña Moreno
Nicolás Peña Moreno

在 Chrome 速度指標團隊中,我們正努力加深瞭解網頁回應使用者輸入內容的速度。我們想要分享一些想法,可以改善回應速度指標並取得您的意見回饋。

本文將涵蓋兩個主要主題:

  1. 查看目前的回應度指標:首次輸入延遲時間 (FID),說明我們選擇 FID,而非部分替代方案的原因。
  2. 說明我們目前正在考慮的改善措施,這應該可以更準確地擷取個別事件的端對端延遲時間。這些改善項目也是為了更全面掌握網頁生命週期內的整體回應速度。

什麼是首次輸入延遲時間?

「First Input Delay (FID)」指標可評估瀏覽器開始處理網頁中初次互動所需的時間。特別是,這項指標會測量使用者與裝置互動的時間,與瀏覽器實際開始處理事件處理常式的時間差。FID 只會評估輕觸和按鍵動作,這表示 FID 只會考量下列第一項事件發生的第一個事件:

  • click
  • keydown
  • mousedown
  • pointerdown (後面必須接上 pointerup)

下圖說明 FID:

「首次輸入延遲時間」是指從輸入開始,到可以處理輸入的時間

FID 不包括執行這些事件處理常式所花費的時間,以及瀏覽器更新畫面後所完成的任何工作。這個指標會測量主要執行緒忙碌時間,再嘗試處理輸入內容。這類封鎖時間通常是因為較長的 JavaScript 工作無法結束,因為這類工作不能隨時停止,因此目前工作必須完成,瀏覽器才能開始處理輸入資料。

我們為何選擇 FID?

我們認為衡量實際使用者體驗非常重要,確保指標在改善後能夠為使用者帶來實際益處。我們選擇評估 FID,是因為 FID 反映出使用者決定與剛剛載入的網站互動時,使用者體驗的一部分。FID 會擷取使用者必須等待一段時間才能看到與網站互動的回應。換句話說,FID 就使用者互動後等待的時間長度下限。

總封鎖時間 (TBT)互動時間 (TTI) 等其他指標是以長時間工作為基礎,與 FID 一樣,也會測量載入期間的主要執行緒封鎖時間。由於可在現場和研究室中評估這些指標,因此許多開發人員都詢問為何不偏好使用其中一項 FID 而非 FID。

有幾個原因可能導致這種情況。也許這些指標無法直接評估使用者體驗,這可能是最重要的原因。這些指標都會評估 JavaScript 在網頁上執行的數量。長時間執行的 JavaScript 往往會對網站造成問題,但如果使用者並未與網頁互動,這些工作也不會影響使用者體驗。 網頁在 TBT 和 TTI 上的分數可能良好,但覺得緩慢或者分數不佳,且使用者覺得速度很快。根據我們的經驗,這些間接測量結果產生的指標適用於部分網站,但對大多數網站來說則不適用。簡單來說,長時間的工作和 TTI 並非以使用者為中心,使得這些較弱的候選項目。

雖然研究室評估指標並非必要,也是診斷工具的寶貴工具,但真正重要的是使用者體驗網站的使用體驗。透過以使用者為中心的指標來反映使用者的實際狀況,您一定能掌握與體驗有關的實用內容。雖然我們知道這部分無法代表完整體驗,但我們決定從一小部分的體驗開始著手。因此,我們設法擷取使用者等待輸入內容處理作業所耗費的較大時間。

測量實地 TTI 的注意事項

評估實際使用者環境中的 TTI 為問題,因為這項作業在頁面載入時太晚發生。必須先設置 5 秒的網路安靜期,才能計算 TTI。在研究室中,當您擁有所需的所有資料時,就可以選擇卸載頁面,但實地人員監控則並非如此。使用者隨時都可以選擇離開頁面或與網頁互動。具體來說,使用者可能會選擇在網頁載入時間較長時離開,因此不會記錄準確的 TTI。我們評估 Chrome 實際使用者的 TTI 時,發現只有大約一半的網頁載入達到 TTI。

我們正在考慮哪些方面的改善?

我們想開發新的指標,將 FID 現有的測量結果延伸至現有的指標,但仍保持與使用者體驗的緊密連結。

我們希望將新指標用於:

  1. 考量所有使用者輸入內容 (不只是第一個輸入內容) 的回應速度
  2. 擷取每個事件的完整時間長度 (不只是延遲)。
  3. 將發生同一邏輯使用者互動的事件歸入同一組,並將互動的延遲時間定義為其中所有事件的持續時間上限。
  4. 建立在頁面上在整個生命週期中發生的所有互動匯總分數。

為了成功,我們必須確信,如果網站在這項新指標的分數不理想,就無法迅速回應使用者互動。

擷取完整活動持續時間

第一個明顯的改進是嘗試擷取事件的端對端延遲時間,如上所述,FID 只會擷取輸入事件的延遲部分。但不會考慮瀏覽器實際處理事件處理常式所需的時間。

事件生命週期包含不同的階段,如下圖所示:

事件生命週期中的五個步驟

Chrome 處理輸入資料的步驟如下:

  1. 系統會產生使用者輸入內容。發生此事件的時間是事件的 timeStamp
  2. 瀏覽器會執行命中測試,以判斷事件屬於哪個 HTML 頁框 (主要頁框或某些 iframe)。然後,瀏覽器會將事件傳送至負責該 HTML 影格的適當轉譯器程序。
  3. 轉譯器接收事件並將其排入佇列,以便在有事件可用時進行處理。
  4. 轉譯器透過執行其處理常式來處理該事件。這些處理常式可能會將其他非同步工作排入佇列,例如 setTimeout 並擷取屬於輸入處理作業的一部分。但此時同步工作已經完成。
  5. 畫面上會繪製一個影格,反映事件處理常式執行的結果。請注意,事件處理常式排入佇列的所有非同步工作可能仍未完成。

上述步驟 (1) 和 (3) 之間的時間間隔為事件的「延遲」,而 FID 會評估這項指標。

上述步驟 (1) 到 (5) 之間的時間是事件的時間長度。這個指標衡量的就是這個指標

事件持續時間包含延遲,但也包括事件處理常式中發生的工作,以及這些處理常式執行後,瀏覽器需要繪製下一個影格所需的工作。在 Event Timing API 中,目前您可透過項目的 duration 屬性查看事件的時間長度。

非同步工作的附註

理想情況下,我們也想要擷取事件觸發的非同步工作。但問題在於,事件觸發的非同步工作定義非常難以正確定義。舉例來說,開發人員可以選擇在事件處理常式上開始一些動畫,然後使用 setTimeout 開始播放這類動畫。如果擷取到處理常式上發布的所有工作,動畫就會隨著動畫持續執行而延遲完成時間。我們認為有必要研究如何運用經驗法則來擷取非同步作業,哪些工作應盡快完成。不過,進行這項程序時請小心謹慎,因為我們不會對需要較長時間完成的工作做出懲處。因此,我們的初步工作會把步驟 5 視為終點:這項作業只會考慮同步工作,以及完成這類工作後繪製所需的時間。也就是說,我們不會運用經驗法則,猜測在步驟 4 中非同步啟動的工作。

值得注意的是,在多數情況下,工作應同步執行。事實上,這可能無法避免,因為事件有時會逐一分派,且事件處理常式必須依序執行。也就是說,我們仍會遺漏重要工作,例如觸發擷取的事件,或需要在下一個 requestAnimationFrame 回呼中執行重要工作的事件。

將事件歸類為互動

將指標測量結果從「延遲」延伸至「時間長度」是很好的第一步,但這仍會讓指標中有一個關鍵差距:重點在於個別事件,而非與網頁互動的使用者體驗。

單一使用者互動可能會觸發許多不同的事件,而個別評估每項事件無法清楚瞭解使用者體驗。我們想要確保指標能盡可能準確擷取使用者輕觸、按下按鍵、捲動及拖曳時的回應時間。因此,我們採用了互動的概念來測量每項互動的延遲時間。

互動類型

下表列出了我們想定義的四種互動,以及與這些互動相關聯的 DOM 事件。請注意,這與發生這類使用者互動時傳送的所有事件集不同。舉例來說,當使用者捲動畫面時,系統會分派捲動事件,但在畫面更新後會發生捲動,因此不會視為互動延遲的一部分。

互動技術 開始 / 結束 電腦活動 行動事件
鍵盤 已按下按鍵 keydown keydown
keypress keypress
已釋出金鑰 keyup keyup
輕觸或拖曳 輕觸「開始」或拖曳開始位置 pointerdown pointerdown
mousedown touchstart
輕觸向上或拖曳結束 pointerup pointerup
mouseup touchend
click mousedown
mouseup
click
捲動 不適用
各種互動類型的 DOM 事件。

上述的前三種互動 (鍵盤、輕觸和拖曳) 目前均受 FID 保護。針對新的回應速度指標,我們也希望加入捲動功能,因為捲動網頁很少,網頁回應不見得容易,這是使用者感受到網頁回應速度的重要因素。

關於開頭與結尾的附註

請注意,每次互動都有兩個部分:使用者按下滑鼠、手指或按下按鍵,以及按下滑鼠左鍵時。我們需要確保,在網頁延遲期間,指標不會將使用者按住手指在這兩個動作之間的時間列入計算!

鍵盤

鍵盤互動有兩個部分:使用者按下按鍵,以及放開按鍵時。與以下使用者互動有三個相關事件:keydownkeyupkeypress。 下圖說明鍵盤互動的 keydownkeyup 延遲和持續時間:

具有不連續事件持續時間的鍵盤互動

在上圖中,由於來自 keydown 更新的影格會在 keyup 發生之前顯示,因此持續時間為不連續。但這不一定是必須一律的情況。此外,請注意,由於產生影格所需的最後一個步驟是在轉譯器程序之外完成,因此可以在轉譯器程序的工作期間顯示影格。

keydownkeypress 會在使用者按下按鍵時發生,而 keyup 會在使用者放開金鑰時發生。一般來說,當使用者按下按鍵時,會發生主要內容更新:畫面上顯示文字或套用修飾符效果。即便如此,我們想要擷取較罕見的 keyup 也會顯示有趣的 UI 更新項目,因此,我們想要查看整體所需時間。

為了擷取鍵盤互動的總時間,我們可以計算 keydownkeyup 事件的持續時間上限。

重複按鍵操作的注意事項

這裡有一個值得一提的極端案例:在某些情況下,使用者可能會按下按鍵,需要一段時間才能釋放。在這種情況下,分派的事件順序可能會不同。在這類情況下,我們會考量每個 keydown 存在一次互動,且不一定有對應的 keyup

輕觸

另一項重要的使用者互動,就是使用者輕觸或點選網站。與 keypress 類似,部分事件會在使用者按下時觸發,而其他事件則會在放開時觸發,如上圖所示。請注意,在電腦和行動裝置上,輕觸動作的相關事件在電腦和行動裝置上略有不同。

點按或點選動作時,發布內容通常是觸發大部分回應的那一個,但與鍵盤互動的情況一樣,我們希望擷取完整的互動。在這種情況下,更重要的是,因為在輕觸時使部分 UI 更新並不常見。

我們希望納入所有這些事件的事件持續時間,但由於許多事件都完全重疊,我們則需要只評估 pointerdownpointerupclick,才能涵蓋完整的互動。

我們可以進一步縮小範圍,只保留 pointerdownpointerup 嗎?

一開始的想法是使用 pointerdownpointerup 事件,並假設這些事件涵蓋我們想要的所有持續時間。可惜的是,這並非個案,正如此邊緣案例所示。請嘗試使用行動裝置或行動裝置模擬開啟這個網站,然後輕觸「按這裡」的訊息。這個網站會觸發瀏覽器輕觸延遲。您可能會發現 pointerdownpointeruptouchend 是快速分派,而 mousedownmouseupclick 則會先等待延遲時間再派出。這表示如果只查看 pointerdownpointerup,就會缺少合成事件的時間長度。這類事件因為瀏覽器輕觸延遲的影響很大,所以應該納入。因此,我們應評估 pointerdownpointerupclick,以涵蓋完整互動。

拖曳

由於拖曳功能具有類似的相關事件,而且通常會導致網站有重要的使用者介面更新,因此我們決定一併納入拖曳功能。但就指標而言,我們只想要考量拖曳開始和拖曳結束,也就是拖曳的初始和最終部分。這樣可以更容易推理理解,並且讓延遲時間能與考慮的其他互動相近。這與我們決定排除連續事件 (例如 mouseover) 的情況一致。

此外,我們也不會考慮透過 Drag and Drop API 實作的拖曳功能,因為這類拖曳僅適用於電腦。

捲動

最常見的網站互動方式之一,就是捲動畫面。就新的指標而言,我們要測量使用者的初始捲動互動延遲時間。具體來說,我們會關注瀏覽器最初的反應,以及使用者要求捲動的事實。我們不會涵蓋整個捲動流程。也就是說,捲動功能會產生許多影格,我們會將焦點集中在當初產生的初始影格,以做為捲動反應。

為何只顯示第一個?如果是一個,後續的影格可能會由個別的流暢提案擷取。也就是說,一旦使用者看到第一個捲動結果,接下來就要評估捲動體驗的流暢度。因此,我們認為流暢的運氣 可能更準確地掌握這個情況因此,與 FID 一樣,我們選擇持續提供獨立的使用者體驗,也就是具有明確時間點的使用者體驗,以及我們可以輕鬆計算延遲時間。整體捲動是連續的體驗,因此我們不打算評估這項指標中的所有項目。

為什麼要測量捲動次數?從 Chrome 收集的捲動效能顯示,捲動速度通常很快。儘管如此,基於各種原因,我們仍希望在新指標中加入初始捲動延遲時間。首先,捲動速度之所以快,是因為已經最佳化許多,因為這樣非常重要。不過,網站還是有方法可以略過瀏覽器提供的部分效能提升。Chrome 最常見的問題是強制捲動發生在主執行緒上。因此,指標應足以表示發生這類情況,並導致使用者捲動效能不佳。第二,捲動畫面特別容易忽略。我們擔心如果排除捲動功能,便會出現很大的盲點,而捲動效能也可能會隨著時間下降,導致網頁開發人員無法清楚察覺。

當使用者捲動畫面時,系統會調派多個事件,例如 touchstarttouchmovescroll。除了捲動事件以外,這主要取決於捲動事件的裝置:在行動裝置上用手指捲動時,系統會分派觸控事件,而使用滑鼠滾輪捲動時,系統會分派觸控事件。初始捲動完成後,就會觸發捲動事件。一般來說,除非網站使用非被動事件監聽器,否則請勿捲動 DOM 事件方塊。我們考慮將捲動視為與 DOM 事件完全分離的。我們要測量的是,從使用者移動到產生捲動手勢,直到顯示捲動畫面的第一個影格為止。

如何定義互動的延遲時間?

如前文所述,為避免歸因使用者按住手指的時間,含有「向下」和「向上」元件的互動需要分別視為不同屬性。

我們希望這些互動類型的延遲時間應該要包含與這些互動相關聯的所有事件的持續時間。由於互動中每個「向下」和「向上」部分的事件持續時間可能會重疊,因此達成這個目標的互動延遲時間最簡單的定義,就是所有相關事件的時間長度上限。如果回到先前的鍵盤圖表,這將是 keydown 時間長度,因為比 keyup 長:

鍵盤互動與持續時間上限醒目顯示

keydownkeyup 的時間長度也可能會重疊。舉例來說,如果兩個事件顯示的影格相同,就可能發生這種情況,如下圖所示:

鍵盤互動是指在同一影格中執行按下和放開按鍵的鍵盤互動

採用上限的方式有一些優缺點,因此我們想要聽取您的意見

  • Pro:這與我們想如何測量捲動指標,只測量單一持續時間值。
  • Pro:這個版本旨在減少鍵盤互動等情況下的雜訊。keyup 通常不會執行任何動作,且使用者可在何處快速或慢速執行按鍵。
  • Con:不會擷取使用者的完整等待時間。舉例來說,它會擷取拖曳的開始或結束,但不會同時擷取兩者。

針對捲動 (只有一個相關事件),我們想要定義其延遲時間,也就是捲動後瀏覽器產生第一個影格所需的時間。也就是說,延遲時間是第一個 DOM 事件 (例如,使用手指的 touchmove) 事件 timeStamp 之間的差距,其大小足以觸發捲動,以及第一個繪製位置的繪製位置。

匯總每個網頁的所有互動

定義互動的延遲時間後,就需要計算網頁載入的匯總值,因為這類值可能有許多使用者互動。取得匯總值可讓我們:

  • 與業務指標建立關聯。
  • 評估與其他成效指標的關聯性。理想情況下,新指標將能充分獨立於現有指標附加價值。
  • 以易於摘要的方式,輕鬆在工具中顯示值。

為了執行這項匯總作業,我們必須解決兩個問題:

  1. 我們會嘗試匯總哪些數據?
  2. Google 該如何匯總這些數據?

我們正在研究和評估數個選項。我們歡迎您對這項匯總作業提供寶貴的意見。

其中一個選項是定義互動延遲時間的預算,這可能會視類型 (捲動、鍵盤、輕觸或拖曳) 而定。例如,如果輕觸預算是 100 毫秒,而輕觸延遲時間為 150 毫秒,那麼該次互動超出預算的金額就是 50 毫秒。接著,我們可以計算超出預算的最長延遲時間,為頁面中任何使用者互動。

另一個選項是計算整個網頁生命週期的互動平均延遲時間或中位數。假設我們的延遲時間是 80 毫秒、90 毫秒和 100 毫秒,那麼網頁的平均延遲時間就是 90 毫秒。此外,我們也會考量平均或「超出預算」的延遲時間,根據互動類型而調整不同的預期。

在網站效能 API 中,這看起來會是什麼樣子?

「活動時間」中缺少哪些項目?

但很遺憾,本文提到的所有想法都無法使用 Event Timing API。特別是,沒有簡單的方法可以得知特定使用者與 API 互動的相關事件。為此,我們提議在 API 中加入 interactionID

Event Timing API 的另一項缺點是無法評估捲動互動,因此我們需要啟用這些評估功能 (透過事件時間或其他 API)。

你現在能試試什麼呢?

目前仍可計算輕觸/拖曳和鍵盤互動的最長延遲時間。下列程式碼片段會產生這兩個指標。

let maxTapOrDragDuration = 0;
let maxKeyboardDuration = 0;
const observer = new PerformanceObserver(list => {
  list.getEntries().forEach(entry => {
    switch(entry.name) {
      case "keydown":
      case "keyup":
        maxKeyboardDuration = Math.max(maxKeyboardDuration,
            entry.duration);
        break;
      case "pointerdown":
      case "pointerup":
      case "click":
        maxTapOrDragDuration = Math.max(maxTapOrDragDuration,
            entry.duration);
        break;
    }
  });
});
observer.observe({type: "event", durationThreshold: 16, buffered: true});
// We can report maxTapDragDuration and maxKeyboardDuration when sending
// metrics to analytics.

意見回饋:

歡迎傳送電子郵件,與我們分享你對這些構想的想法:web-vitals-feedback@googlegroups.com!