長動畫頁框 API

Long Animation Frames API (LoAF - 發音的 Lo-Af) 是 Long Tasks API 的更新版本,目的是協助您掌握緩慢的使用者介面 (UI) 更新情形。這項指標有助於找出可能影響「與下一次繪製互動」(INP) Core Web Vitals 指標評估回應速度的緩慢動畫影格,或是找出其他會影響流暢度的 UI 卡頓情形。

API 狀態

瀏覽器支援

  • 123
  • x
  • x
  • x

隨著從 Chrome 116 版開始試用到 Chrome 122 版,LoAF API 已從 Chrome 123 版推出

Long Tasks API

瀏覽器支援

  • 58
  • 79
  • x
  • x

資料來源

Long Animation Frames API 取代了 Long Tasks API,它目前已在 Chrome 中提供一段時間 (從 Chrome 58 以上版本開始)。顧名思義,Long Task API 可以監控長時間工作,也就是佔用主執行緒 50 毫秒或更久的工作。您可以使用 PerformanceLongTaskTiming 介面和 PeformanceObserver 來監控長時間的工作:

const observer = new PerformanceObserver((list) => {
  console.log(list.getEntries());
});

observer.observe({ type: 'longtask', buffered: true });

時間較長的工作可能會導致回應問題。如果使用者嘗試與頁面互動,例如點選按鈕或開啟選單,但主執行緒正在進行長時間的工作,使用者互動會延遲等待工作完成。

為了加快回應速度,建議您分解長時間的工作。如果每項長篇工作分別拆成一系列較小的工作,則可能會在這些工作之間執行更重要的工作,以免回應互動時的嚴重延遲。

因此,嘗試改善回應速度時,首先要做的就是執行效能追蹤記錄,然後查看較長的工作。無論是透過 Lighthouse 研究室 (例如避免長時間的主執行緒工作稽核) 或查看 Chrome 開發人員工具中的長時間工作,都可以使用這項工具。

使用研究室測試時,通常無法找出回應速度的問題,因為這些工具不一定包括互動,如果互動,那麼只是互動的一小部分。最好評估實際互動速度緩慢的原因。

Long Tasks API 的缺點

使用 Performance Observer 評估實地中的長期工作是非常實用的做法。事實上,除了長時間工作中已發生,以及花費的時間,應用程式沒有提供充分的資訊。

實際使用者監控 (RUM) 工具經常利用這項功能,將長時間工作的數量或持續時間繪製成趨勢,或判斷這些動作所在網頁。然而,如果沒有造成長時間工作的基本詳細資料,這種工具只會使用有限的功能。Long Tasks API 提供「基本歸因模式」,最好只告知您容器發生中的長時間工作 (頂層文件或 <iframe>),但無法查看呼叫該 API 的指令碼或函式,如一般項目所示:

{
  "name": "unknown",
  "entryType": "longtask",
  "startTime": 31.799999997019768,
  "duration": 136,
  "attribution": [
    {
      "name": "unknown",
      "entryType": "taskattribution",
      "startTime": 0,
      "duration": 0,
      "containerType": "window",
      "containerSrc": "",
      "containerId": "",
      "containerName": ""
    }
  ]
}

Long Tasks API 也不完整,因為這個 API 可能會排除某些重要工作,在理想情況下,有些更新 (例如轉譯) 應與先前的執行作業一起納入,導致更新準確評估該互動的「工作總數」。如要進一步瞭解依賴工作的限制,請參閱說明的「『長時間工作』不足」一節

最後一個問題是,若執行長時間的工作只回報超過 50 毫秒限制的個別工作,動畫頁框可能由多項工作組成,低於 50 毫秒的上限,但整體仍然會封鎖瀏覽器的轉譯功能。

長動畫頁框 API

瀏覽器支援

  • 123
  • x
  • x
  • x

Long Animation Frames API (LoAF) 是全新的 API,旨在解決 Long Tasks API 的某些缺點,協助開發人員取得更多可做為行動依據的洞察資料,藉此解決回應問題及改善 INP。

「良好回應」是指網頁能快速回應與網頁的互動。要達成這項目標,就必須能適時繪製使用者需要的更新內容,並避免阻礙這些更新。如果是 INP,建議在 200 毫秒內回應,但針對其他更新 (例如動畫),即使時間過長也無妨。

Long Animation Frames API 是測量封鎖工作的另一種方法。顧名思義,Long Animation Frames API 並非測量個別「工作」,而是測量「長動畫影格」。較長的動畫影格是指算繪更新的延遲超過 50 毫秒 (與 Long Tasks API 的門檻相同)。

觀察長動畫影格的方式和具有 PerformanceObserver 的長時間工作類似,但是改為查看 long-animation-frame 類型:

const observer = new PerformanceObserver((list) => {
  console.log(list.getEntries());
});

observer.observe({ type: 'long-animation-frame', buffered: true });

您也可以透過效能時間軸查詢先前的長動畫影格,如下所示:

const loafs = performance.getEntriesByType('long-animation-frame');

不過,效能項目的 maxBufferSize 會在此後捨棄較新的項目,因此建議您使用 PerformanceObserver 方法。long-animation-frame 緩衝區空間設為 200,與 long-tasks 相同。

透過檢視影格而非工作的優點

從畫格的角度來看,這麼做的主要優點是,長動畫可以由任意數量的動畫組成,以累計長的動畫影格呈現。這解決了上述最後一點,因為 Long Tasks API 無法顯示在動畫影格之前,需要轉譯的多個較小工作的總和。

對於長時間工作,這個替代檢視畫面還有一個優點,就是可以提供整個影格的時間細目。LoAF 不只加入 startTimeduration (例如 Long Tasks API),更詳細地細分了影格持續時間的各個部分,包括:

  • startTime:長動畫影格相對於導覽開始時間的開始時間。
  • duration:長動畫影格的時間長度 (不含顯示時間)。
  • renderStart:轉譯週期的開始時間,其中包括 requestAnimationFrame 回呼、樣式和版面配置計算、調整大小觀察器,以及交集觀察器回呼。
  • styleAndLayoutStart:用於計算樣式和版面配置的時間時間範圍的開始時間。
  • firstUIEventTimestamp:這個影格期間要處理的第一個 UI 事件 (滑鼠/鍵盤等) 的時間。
  • blockingDuration:動畫影格遭封鎖的時間長度 (以毫秒為單位)。

這些時間戳記可將較長的動畫影格分割成時間:

時間 計算
開始時間 startTime
結束時間 startTime + duration
工作時間長度 renderStart ? renderStart - startTime : duration
轉譯作業時間長度 renderStart ? (startTime + duration) - renderStart: 0
顯示:預先版面配置時間長度 styleAndLayoutStart ? styleAndLayoutStart - renderStart : 0
算繪:樣式和版面配置時間長度 styleAndLayoutStart ? (startTime + duration) - styleAndLayoutStart : 0

如要進一步瞭解這些個別時間點,請參閱說明,其中提供更詳細的詳細資料,瞭解哪個活動影響長動畫影格。

改善歸因分析

long-animation-frame 項目類型會針對造成動畫影格時間最長的指令碼提供更精確的歸因資料。

與 Long Tasks API 類似,這項資訊會透過歸因項目陣列提供,詳細資料如下:

  • nameEntryType 都會傳回 script
  • 有意義的 invoker,表示指令碼的呼叫方式 (例如 'IMG#id.onload''Window.requestAnimationFrame''Response.json.then')。
  • 指令碼進入點的 invokerType
    • user-callback:從網路平台 API 註冊的已知回呼 (例如 setTimeoutrequestAnimationFrame)。
    • event-listener:平台事件的事件監聽器 (例如 clickloadkeyup)。
    • resolve-promise:平台承諾的處理常式 (例如 fetch()。請注意,在承諾的情況下,相同承諾的所有處理常式會混合為一個「指令碼」).
    • reject-promise:按照 resolve-promise 所述,但在遭到拒絕的情況下。
    • classic-script:指令碼評估 (例如 <script>import())
    • module-script:與 classic-script 相同,但適用於模組指令碼。
  • 該指令碼的時間資料會分開:
    • startTime:叫用項目函式的時間。
    • duration:從 startTime 到後續微工作佇列處理完成的持續時間。
    • executionStart:編譯後的時間。
    • forcedStyleAndLayoutDuration:在這個函式中處理強製版面配置/樣式所花費的總時間 (請參閱「輾轉現象」一節)。
    • pauseDuration:「暫停」同步作業 (快訊、同步 XHR) 的總時長。
  • 指令碼來源詳細資料:
    • sourceURL:有可用的指令碼資源名稱 (如果找不到,則為空白)。
    • sourceFunctionName:有可用的指令碼函式名稱 (如果找不到,則為空白)。
    • sourceCharPosition:指令碼字元位置在適用情況下,如未找到,則為 -1。
  • windowAttribution:發生較長的動畫頁框容器 (頂層文件或 <iframe>)。
  • window:相同來源視窗的參照。

如有提供來源項目,開發人員便可明確瞭解在呼叫指令碼中,每個指令碼的呼叫方式。以便在 JavaScript 資源中指出產生長動畫影格的確切位置。

long-animation-frame 效能項目範例

完整的 long-animation-frame 效能項目範例 (含單一指令碼) 如下:

{
  "blockingDuration": 0,
  "duration": 60,
  "entryType": "long-animation-frame",
  "firstUIEventTimestamp": 11801.099999999627,
  "name": "long-animation-frame",
  "renderStart": 11858.800000000745,
  "scripts": [
    {
      "duration": 45,
      "entryType": "script",
      "executionStart": 11803.199999999255,
      "forcedStyleAndLayoutDuration": 0,
      "invoker": "DOMWindow.onclick",
      "invokerType": "event-listener",
      "name": "script",
      "pauseDuration": 0,
      "sourceURL": "https://web.dev/js/index-ffde4443.js",
      "sourceFunctionName": "myClickHandler",
      "sourceCharPosition": 17796,
      "startTime": 11803.199999999255,
      "window": [Window object],
      "windowAttribution": "self"
    }
  ],
  "startTime": 11802.400000000373,
  "styleAndLayoutStart": 11858.800000000745
}

正如你所見,這為網站提供了前所未有的資料量,因此能夠瞭解轉譯速度緩慢的原因。

啟用 Long Animation Frame API

自 Chrome 123 版起,系統會預設啟用 Long Animation Frames API。

在欄位中使用 Long Animation Frames API

Lighthouse 等工具 (雖然有助於發現及重現問題) 也能派上用場,但這類工具可能會遺漏使用者體驗的重要面向,而只有實際資料才能提供。您可在欄位中使用 Long Animation Frames API 收集重要的使用者互動資料,以提供 Long Tasks API 無法做到的使用者互動。這麼做有助於顯示及重現互動問題,以便您再次發現這些問題。

以下列出一些建議策略,但 Chrome 團隊非常樂意聽取這個 API 的意見回饋,以及開發人員和 RUM 供應商會透過 API 提供的資訊看到自己的想法。

可偵測長動畫頁框 API 的功能

您可以使用以下程式碼測試 API 是否支援:

if (PerformanceObserver.supportedEntryTypes.includes('long-animation-frame')) {
  // Monitor LoAFs
}

根據預設,系統目前不支援長動畫影格,且處於此轉場狀態時,您可以使用以下替代選項:

if ('PerformanceLongAnimationFrameTiming' in window) {
  // Monitor LoAFs
}

將長動畫資料回報回分析端點

如圖所示,LoAF 成效項目包含寶貴資訊。其中一種策略是監控所有 AF

const REPORTING_THRESHOLD_MS = 150;

const observer = new PerformanceObserver(list => {
  for (const entry of list.getEntries()) {
    if (entry.duration > REPORTING_THRESHOLD_MS) {
      // Example here logs to console, but could also report back to analytics
      console.log(entry);
    }
  }
});
observer.observe({ type: 'long-animation-frame', buffered: true });

因為長動畫影格項目可能會相當大,開發人員應該決定要將項目中的哪些資料傳送至數據分析。例如項目摘要時間,也可能是指令碼名稱,或可能認定為必要的其他最低內容比對資料組合。

觀察最差的長動畫影格

網站可能會想要收集動畫最長畫面 (或影格) 的資料,以便減少需要信標的資料量。因此,無論網頁體驗有多長的動畫播放時間長短,只有最差的一、五,或無論如何需要多少長的動畫影格的資料都會被信標放回。

MAX_LOAFS_TO_CONSIDER = 10;
let longestBlockingLoAFs = [];

const observer = new PerformanceObserver(list => {
  longestBlockingLoAFs = longestBlockingLoAFs.concat(list.getEntries()).sort(
    (a, b) => b.blockingDuration - a.blockingDuration
  ).slice(0, MAX_LOAFS_TO_CONSIDER);
});
observer.observe({ type: 'long-animation-frame', buffered: true });

等到適當的時間 (最好發生 visibilitychange 事件) 指標回歸數據分析資料。針對本機測試,您可以定期使用 console.table

console.table(longestBlockingLoAFs);

連結至最長的 INP 互動

為觀察最壞的 LoAF,與 INP 項目對應的 LoAF 框架可做為歸因資料,讓您進一步進一步瞭解如何改善 INP。

目前沒有直接 API 可連結 INP 項目與相關的 LoAF 項目或項目,但您可以在程式碼中比較每個項目的開始和結束時間 (請參閱此範例指令碼)。

回報包含互動的長動畫影格

您也可以改用需要較少程式碼的替代方法,一律傳送最大 (或前 X 大) 的 LoAF 項目,以便在影格期間發生互動 (可透過 firstUIEventTimestamp 值偵測)。在大多數情況下,這將包括特定造訪的 INP 互動,但在極少數情況下,由於這可能不是其他使用者的 INP 互動,所以仍未顯示需要修正的長時間互動。

下列程式碼會記錄影格期間有互動發生的所有 LoAF 項目 (超過 150 毫秒)。此處選擇 150,因為略低於 200 毫秒的「良好」INP 門檻。您可以根據需求,選擇較高或較低的值。

const REPORTING_THRESHOLD_MS = 150;

const observer = new PerformanceObserver(list => {
    for (const entry of list.getEntries()) {
      if (entry.duration > REPORTING_THRESHOLD_MS &&
        entry.firstUIEventTimestamp > 0
      ) {
        // Example here logs to console, but could also report back to analytics
        console.log(entry);
      }
    }
});
observer.observe({ type: 'long-animation-frame', buffered: true });

找出長動畫影格中的常見模式

替代策略是參考最常出現在長動畫影格項目中的常見指令碼。系統可能以指令碼和/或字元位置層級記錄資料,找出屢次違規者。

對可自訂的平台來說,這個做法特別實用。因為在許多不同的網站上,主題或外掛程式較容易找出造成效能問題的主題或外掛程式。

系統在較長的動畫頁框中,可以將常見指令碼 (或第三方來源) 的執行時間加總,然後回報給這類畫面,找出整個網站或一組網站中長動畫影格的常見貢獻者。查看網址範例:

const observer = new PerformanceObserver(list => {
  const allScripts = list.getEntries().flatMap(entry => entry.scripts);
  const scriptSource = [...new Set(allScripts.map(script => script.sourceURL))];
  const scriptsBySource= scriptSource.map(sourceURL => ([sourceURL,
      allScripts.filter(script => script.sourceURL === sourceURL)
  ]));
  const processedScripts = scriptsBySource.map(([sourceURL, scripts]) => ({
    sourceURL,
    count: scripts.length,
    totalDuration: scripts.reduce((subtotal, script) => subtotal + script.duration, 0)
  }));
  processedScripts.sort((a, b) => b.totalDuration - a.totalDuration);
  // Example here logs to console, but could also report back to analytics
  console.table(processedScripts);
});

observer.observe({type: 'long-animation-frame', buffered: true});

輸出範例如下:

(index) sourceURL count totalDuration
0 'https://example.consent.com/consent.js' 1 840
1 'https://example.com/js/analytics.js' 7 628
2 'https://example.chatapp.com/web-chat.js' 1 5

在工具中使用 Long Animation Frames API

此 API 可能也允許其他開發人員工具進行本機偵錯。雖然 Lighthouse 和 Chrome 開發人員工具等工具能夠透過較低層級的追蹤記錄詳細資料收集大量資料,但這個較高層級的 API 或許可讓其他工具存取這項資料。

在開發人員工具中顯示長動畫影格資料

您可以使用 performance.measure() API 在開發人員工具中顯示較長的動畫影格,該 API 會於效能追蹤記錄在開發人員工具的使用者時間軌中顯示,讓您瞭解需要在哪些方面改善效能:

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    performance.measure('LoAF', {
      start: entry.startTime,
      end: entry.startTime + entry.duration,
    });
  }
});

observer.observe({ type: 'long-animation-frame', buffered: true });

如果這個 API 長期下來非常實用,可能會納入開發人員工具本身,但舊版的程式碼片段允許同時顯示。

在其他開發人員工具中使用長動畫影格資料

網站體驗指標擴充功能已在記錄摘要偵錯資訊中顯示值,以便診斷效能問題。API 推出後,這類工具可更輕鬆地顯示資料,協助開發人員瞭解哪些事項應集中精力。我們也計劃將這項功能加入第 4 版的 Web Vitals JavaScript 程式庫

在自動測試工具中使用長動畫影格資料

同樣地,無論是在 CI/CD 管道中,您也可能會在執行各種測試套件時測量較長的動畫影格,藉此顯示潛在的效能問題詳細資料。

常見問題

以下是一些有關此 API 的常見問題:

為何不只要擴充或疊代 Long Tasks API?

這個替代做法會回報潛在回應問題,但最終卻截然不同。請務必確保仰賴現有 Long Tasks API 的網站能繼續正常運作,以免中斷現有應用實例。

儘管 Long Tasks API 能受益於 LoAF 的部分功能 (例如更強大的歸因模式),但我們深信把重點放在影格而非任務上,可帶來許多好處,使這個 API 與現有 Long Tasks API 有截然不同的 API。

這項功能會取代 Long Tasks API 嗎?

雖然我們認為 Long Animation Frames API 比較適合用來評估長時間的工作,更具有更完整的 API,但我們目前並沒有計劃淘汰 Long Tasks API。

想提供意見

您可以在 GitHub 問題清單中查看意見回饋;如果在 Chrome 實作 API 時發生錯誤,可以透過 Chrome 的問題追蹤工具提交。

結論

Long Animation Frames API 是令人期待的全新 API,與舊版 Long Tasks API 具備許多優點。

這證明是 INP 衡量的回應速度問題的關鍵工具。INP 是改善效能的一大挑戰,Chrome 團隊希望能幫助開發人員找出及解決問題。

但 Long Animation Frames API 的範圍不只在 INP,還能協助找出更新速度較慢的其他原因,這些原因可能會影響網站的整體使用者體驗。

特別銘謝

Henry BeUnsplash 提供的縮圖。