改善與下一個顯示的內容的互動方式

瞭解如何最佳化網站的 Interaction to Next Paint。

Interaction to Next Paint (INP)穩定的 Core Web Vitals 指標,可評估網頁回應使用者互動的整體效能,方法是觀察使用者造訪網頁期間,網頁對所有符合資格的互動的延遲時間。最終 INP 值是指觀測到的最長互動時間 (有時會忽略離群值)。

為了提供良好的使用者體驗,網站應力求 Interaction to Next Paint 為 200 毫秒以下。為確保多數使用者都能享有此等級的體驗,建議您以網頁載入的第 75 個百分位數做為評估門檻,並區分行動裝置和電腦裝置。

良好的 INP 值為 200 毫秒以下,不良的值則為 500 毫秒以上,介於兩者之間的值則需要改善。

視網站而定,互動可能很少,甚至完全沒有,例如網頁主要由文字和圖片組成,互動元素很少或完全沒有。或者,如果是文字編輯器或遊戲等網站,可能會有數百或數千個互動。無論是哪種情況,如果 INP 過高,使用者體驗就會受到影響。

改善 INP 需要花費時間和心力,但成效是提供更優質的使用者體驗。本指南將說明改善 INP 的方法。

找出導致 INP 不佳的原因

您必須先取得資料,才能判斷網站的 INP 是否不佳或需要改善,進而修正互動速度緩慢的問題。取得這些資訊後,您就可以進入實驗室,開始診斷互動速度緩慢的問題,並找出解決方法。

找出現場的互動速度緩慢問題

在理想情況下,您應先從實地資料開始,進行 INP 最佳化。實境使用者監控 (RUM) 供應商提供的欄位資料,不僅可提供網頁的 INP 值,還可提供情境資料,指出哪些特定互動導致 INP 值,以及互動是否發生在網頁載入期間或之後、互動類型 (點擊、按鍵或輕觸) 等其他重要資訊。

如果您不依賴 RUM 供應商取得現場資料,INP 現場資料指南建議透過 PageSpeed Insights 使用 Chrome 使用者體驗報告 (CrUX),以便填補空白。CrUX 是 Core Web Vitals 計畫的官方資料集,提供數百萬個網站 (包括 INP) 的指標概要。不過,CrUX 通常不會提供您從 RUM 供應商取得的背景資料,以利分析問題。因此,我們仍建議網站盡可能使用 RUM 供應商,或實作自己的 RUM 解決方案,以補足 CrUX 提供的功能。

在實驗室中診斷互動速度緩慢的問題

理想情況下,您應該在有實地資料顯示互動速度緩慢時,才開始在實驗室進行測試。在沒有實地資料的情況下,您可以採用一些策略,在實驗室中找出互動速度緩慢的問題。這類策略包括追蹤常見的使用者流程並測試互動情形,以及在載入期間與網頁互動 (此時主執行緒通常最繁忙),以便在使用者體驗的關鍵階段顯示互動速度緩慢的情形。

改善互動

找出互動速度緩慢的問題後,如果您可以在實驗室中手動重現問題,下一步就是進行最佳化。互動可分為三個階段:

  1. 輸入延遲時間:從使用者開始與網頁互動開始計算,結束時間則是互動事件回呼開始執行時。
  2. 處理時間,包括事件回呼執行完畢所需的時間。
  3. 呈現延遲,即瀏覽器呈現下一個影格所需的時間,其中包含互動內容的視覺結果。

這三個階段的總和就是總互動延遲時間。互動過程的每個階段都會對總互動延遲時間造成影響,因此請務必瞭解如何最佳化互動的各個部分,讓互動執行時間盡可能縮短。

找出並減少輸入延遲

當使用者與網頁互動時,第一個互動部分是輸入延遲時間。視網頁上的其他活動而定,輸入延遲時間可能會相當長。這可能是因為主執行緒上發生的活動 (可能是因為指令碼載入、剖析和編譯)、擷取處理、計時器函式,甚至是其他快速連續發生且彼此重疊的互動所致。

無論互動輸入延遲的來源為何,您都應盡可能減少輸入延遲,以便互動作業盡快開始執行事件回呼。

指令碼評估與啟動期間的長時間工作之間的關係

在頁面生命週期中,互動性的關鍵部分是啟動期間。網頁載入時會先進行轉譯,但請務必記住,即使網頁已轉譯,也不代表網頁已完成載入。視網頁需要多少資源才能完全運作而定,使用者可能會在網頁載入期間嘗試與網頁互動。

在網頁載入期間,延長互動輸入延遲時間的一個原因是指令碼評估。從網路擷取 JavaScript 檔案後,瀏覽器仍須執行一些工作,才能執行 JavaScript,這些工作包括剖析指令碼以確保其語法有效、將其編譯為位元碼,然後執行。

視指令碼大小而定,這項工作可能會在主執行緒上引入長時間的工作,導致瀏覽器延遲回應其他使用者互動。為了讓網頁在載入期間能回應使用者輸入內容,請務必瞭解如何減少在網頁載入期間執行長時間任務的可能性,以便讓網頁保持快速。

最佳化事件回呼

輸入延遲時間只是 INP 測量結果的第一部分。您也必須確保在回應使用者互動時執行的事件回呼,可以盡快完成。

經常將控制權交給主執行緒

在最佳化事件回呼時,最佳的一般建議是盡量減少回呼中的工作量。不過,互動邏輯可能很複雜,您可能只能稍微減少互動邏輯的工作量。

如果發現您的網站也是這種情況,下一個可嘗試的方法是將事件回呼中的作業拆解為個別工作。這可避免集體工作變成長時間的工作,進而阻斷主執行緒,讓其他等待主執行緒的互動更快執行。

setTimeout 是分割工作的一種方法,因為傳遞至該函式的回呼會在新工作中執行。您可以單獨使用 setTimeout,也可以將其用途抽象化為個別函式,以便更符合人體工學

雖然隨意產生產值比完全不產生產值好,但您可以使用更精細的方式產生主要執行緒,只在更新使用者介面的事件回呼後立即產生產值,讓算繪邏輯可以更快執行。

產生收益,讓轉譯工作提早進行

更進階的讓步技巧包括在事件回呼中建構程式碼,以便限制執行的內容,只限於為下一個影格套用視覺更新所需的邏輯。其他所有內容都可以延後至後續工作。這不僅可讓回呼輕巧靈活,還可改善互動顯示時間,因為系統不會允許視覺更新阻斷事件回呼程式碼。

舉例來說,假設有個富文本編輯器,會在您輸入文字時設定文字格式,並根據您輸入的內容更新 UI 的其他部分 (例如字數、醒目顯示拼字錯誤,以及其他重要的視覺回饋)。此外,應用程式可能也需要儲存您輸入的內容,以免您離開應用程式後,當您返回時,您輸入的內容就會遺失。

在這個範例中,系統需要根據使用者輸入的字元執行下列四項操作。不過,您只需要在下一個影格顯示前完成第一個項目。

  1. 將使用者輸入的內容更新至文字方塊,並套用任何必要的格式設定。
  2. 更新 UI 中顯示目前字數的部分。
  3. 執行邏輯,檢查拼寫是否有誤。
  4. 儲存最新變更 (在本機或遠端資料庫中)。

執行這項操作的程式碼可能會如下所示:

textBox.addEventListener('input', (inputEvent) => {
  // Update the UI immediately, so the changes the user made
  // are visible as soon as the next frame is presented.
  updateTextBox(inputEvent);

  // Use `setTimeout` to defer all other work until at least the next
  // frame by queuing a task in a `requestAnimationFrame()` callback.
  requestAnimationFrame(() => {
    setTimeout(() => {
      const text = textBox.textContent;
      updateWordCount(text);
      checkSpelling(text);
      saveChanges(text);
    }, 0);
  });
});

下圖呈現了延後非必要更新至下一個影格後的效果,可縮短處理時間,進而縮短整體互動延遲時間。

這張圖片顯示兩種情境下的鍵盤互動和後續工作。在頂端圖表中,呈現關鍵工作和所有後續背景工作會同步執行,直到顯示影格時機到來為止。在底部圖片中,系統會先執行重要的轉譯作業,然後交給主執行緒,以便更快呈現新影格。背景任務會在之後執行。
點選上方圖片可查看高解析度版本。

雖然在先前的程式碼範例中,在 requestAnimationFrame() 呼叫中使用 setTimeout() 的做法確實有點難懂,但這是一種有效的方法,可在所有瀏覽器中運作,確保非關鍵程式碼不會封鎖下一個影格。

避免版面配置輾轉

版面配置衝突 (有時稱為強制同步版面配置) 是一種轉譯效能問題,會在版面配置同步發生時發生。這種情況會在您在 JavaScript 中更新樣式,然後在同一個工作中讀取樣式時發生,而且 JavaScript 中有許多屬性可能會導致版面配置耗盡資源

如 Chrome 開發人員工具的「效能」面板所示,這張圖表顯示版面配置耗盡效能的視覺化效果。
如 Chrome 開發人員工具的「效能」面板所示,這是版面配置衝突的範例。涉及版面配置耗損的轉譯工作會在呼叫堆疊的部分右上角顯示紅色三角形,通常會標示為「Recalculate Style」或「Layout」

版面配置衝突是效能瓶頸,因為更新樣式後,系統會立即在 JavaScript 中要求這些樣式的值,導致瀏覽器必須同步執行版面配置作業,而這項作業原本可以等待事件回呼執行完畢後再以非同步方式執行。

盡量縮短呈現延遲時間

互動標記的呈現延遲,是指從互動事件回呼完成執行到瀏覽器能夠繪製下一個影格,顯示產生的視覺變更之間的時間。

盡量縮小 DOM 大小

如果網頁的 DOM 很小,轉譯作業通常會很快完成。不過,當 DOM 變得非常大時,轉譯工作會隨著 DOM 大小增加而擴大。轉譯工作與 DOM 大小之間的關係並非線性關係,但大型 DOM 的轉譯工作量確實比小型 DOM 多。大型 DOM 有兩種情況會造成問題:

  1. 在初始網頁轉譯期間,大型 DOM 需要大量工作才能轉譯網頁的初始狀態。
  2. 回應使用者互動時,大型 DOM 可能會導致轉譯更新的成本過高,進而增加瀏覽器呈現下一個影格所需的時間。

請注意,在某些情況下,大型 DOM 無法大幅縮減。雖然您可以採取一些方法來縮減 DOM 大小,例如扁平化 DOM在使用者互動期間新增 DOM,以便維持初始 DOM 大小,但這些技巧可能只能達到一定程度。

使用 content-visibility 延遲轉譯畫面外元素

您可以使用 CSS content-visibility 屬性,在網頁載入期間和回應使用者互動時,限制算繪工作量,這相當於在元素接近可視區域時,以延後算繪的方式進行。雖然 content-visibility 需要一些練習才能有效使用,但如果結果是縮短算繪時間,進而改善網頁的 INP,就值得一試。

使用 JavaScript 算繪 HTML 時,請留意效能成本

只要有 HTML,就會有 HTML 剖析作業。瀏覽器剖析 HTML 並將其轉換為 DOM 後,就必須套用樣式、執行版面配置計算,並接著轉譯該版面配置。這是不可避免的成本,但您如何轉譯 HTML 會影響成本。

伺服器傳送 HTML 時,瀏覽器會以串流的形式接收。串流是指伺服器傳送的 HTML 回應會分成多個區塊傳送。瀏覽器會在串流內容抵達時逐步剖析串流內容的片段,然後逐漸轉譯,藉此最佳化串流內容的處理方式。這是一種效能最佳化做法,可讓瀏覽器在網頁載入期間自動定期產生隱含的效能,而且您不必付費。

雖然第一次造訪任何網站時都會涉及一些 HTML,但常見的做法是先從最少的初始 HTML 開始,然後再使用 JavaScript 填入內容區域。使用者互動後,系統也會針對該內容區塊進行後續更新。這通常稱為單頁應用程式 (SPA) 模型。此模式的一個缺點是,如果您在用戶端上使用 JavaScript 算繪 HTML,不僅會產生 JavaScript 處理作業的成本,且瀏覽器會在完成剖析 HTML 並算繪後才「不會」產生。

不過,請務必記住,即使網站不是 SPA,也可能會透過 JavaScript 將部分 HTML 算繪成互動結果。只要您不會在用戶端轉譯大量 HTML,這通常不會造成問題,因為這可能會延遲下一個影格的呈現。不過,請務必瞭解這種在瀏覽器中算繪 HTML 的做法對效能造成的影響,以及如果您透過 JavaScript 算繪大量 HTML,這會如何影響網站對使用者輸入內容的回應速度。

結論

改善網站的 INP 是一項疊代程序。修正互動速度緩慢的問題後,您很可能會發現其他互動速度緩慢的問題,尤其是如果您的網站提供大量互動功能,就更有可能發生這種情況。因此,您也需要對這些互動進行最佳化。

改善 INP 的關鍵在於持之以恆。隨著時間的推移,您可以讓網頁的回應速度達到使用者滿意的程度。當您為使用者開發新功能時,也可能需要進行相同的程序,針對他們的互動進行最佳化。這項作業需要花費時間和心力,但絕對值得。

主頁橫幅圖片取自 Unsplash,由 David Pisnoy 拍攝,並根據 Unsplash 授權進行修改。