長いアニメーション フレーム API

Long Animation Frames API(LoAF)は、ユーザー インターフェース(UI)の更新の遅延をより正確に把握できるように Long Tasks API を更新したものです。これは、応答性を測定する Core Web Vitals 指標である Interaction to Next Paint(INP)に影響する可能性のあるアニメーション フレームの遅延を特定したり、スムーズさに影響する他の UI ジャンクを特定したりするのに役立ちます。

API のステータス

対応ブラウザ

  • Chrome: 123。
  • Edge: 123。
  • Firefox: サポートされていません。
  • Safari: サポートされていません。

ソース

Chrome 116 ~ Chrome 122 のオリジン トライアルに続いて、LoAF API は Chrome 123 からリリースされました。

バックグラウンド: Long Tasks API

対応ブラウザ

  • Chrome: 58.
  • Edge: 79。
  • Firefox: サポートされていません。
  • Safari: サポートされていません。

ソース

Long Animation Frames API は、Chrome で長い間(Chrome 58 以降)利用可能な Long Tasks API の代替手段です。その名が示すように、Long Task API では、メインスレッドを 50 ミリ秒以上占有する長いタスクをモニタリングできます。長時間のタスクは、PeformanceObserver を使用して PerformanceLongTaskTiming インターフェースでモニタリングできます。

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

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

長時間のタスクは、応答性の問題を引き起こす可能性があります。ユーザーがページの操作(ボタンのクリックやメニューの開くなど)を試みても、メインスレッドがすでに長いタスクを処理している場合、そのタスクが完了するまでユーザーの操作が遅延します。

応答性を高めるには、長いタスクを分割することをおすすめします。時間のかかるタスクを複数の小さなタスクに分割することで、その間により重要なタスクを実行できるため、インタラクションへの応答が大幅に遅れることがありません。

そのため、応答性を改善しようとする際、多くの場合、最初の作業はパフォーマンス トレースを実行して長時間のタスクを確認することです。たとえば、Lighthouse などのラボベースの監査ツール(長いメインスレッド タスクを回避する監査があります)を使用するか、Chrome DevTools で長いタスクを確認します。

ラボベースのテストは、レスポンシビリティの問題を特定する際には適切でないことが多いため、これらのツールにインタラクションが含まれていない場合があります。含まれている場合でも、想定されるインタラクションのサブセットにすぎません。理想的には、フィールドでインタラクションの遅延の原因を測定します。

Long Tasks API の欠点

現場での時間のかかるタスクを Performance Observer で測定しても、役に立つ場面はそれほど多くありません。実際には、長いタスクが発生したという事実と、その時間以外に多くの情報は得られません。

リアルユーザー モニタリング(RUM)ツールでは、この情報を使用して長時間のタスクの数や所要時間の傾向を把握したり、長時間のタスクが発生したページを特定したりすることがよくあります。しかし、長時間のタスクの原因に関する詳細情報がないと、この情報は限定的な用途しかありません。Long Tasks API には基本的なアトリビューション モデルのみが用意されています。このモデルでは、長時間タスクが発生したコンテナ(最上位ドキュメントまたは <iframe>)のみが判明し、呼び出したスクリプトや関数は判明しません。これは、次の典型的なエントリで示されています。

{
  "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 も不完全なビューです。一部の重要なタスクが除外される可能性があるためです。レンダリングなどの更新は、別々のタスクで行われますが、理想的には、その更新を引き起こした前の実行とともに、そのインタラクションの「合計作業量」を正確に測定するために、そのタスクを含める必要があります。タスクに依存する制限事項の詳細については、説明の「長いタスクの限界」セクションをご覧ください。

最後の問題は、長いタスクを測定しても、50 ミリ秒の制限を超える個々のタスクのみがレポートされることです。アニメーション フレームは、この 50 ミリ秒の制限よりも小さい複数のタスクで構成されている場合がありますが、それでもブラウザのレンダリング能力をブロックする可能性があります。

Long Animation Frames API

対応ブラウザ

  • Chrome: 123。
  • Edge: 123。
  • Firefox: サポートされていません。
  • Safari: サポートされていません。

ソース

Long Animation Frames API(LoAF)は、Long Tasks API の欠点の一部に対処することを目的とした新しい API です。これにより、デベロッパーは、応答性の問題に対処し、INP を改善するために、より実用的な分析情報を入手できるようになります。また、スムーズさに関する問題に関する分析情報も入手できます。

応答性が良好とは、ページが操作に対して迅速に応答することを意味します。つまり、ユーザーが必要とする更新をタイムリーにペイントし、これらの更新がブロックされないようにする必要があります。INP の場合は200 ミリ秒以内にレスポンスを返すことをおすすめしますが、他の更新(アニメーションなど)の場合は 200 ミリ秒でも長すぎる場合があります。

Long Animation Frames API は、ブロック作業を測定する代替手段です。Long Animation Frames API は、その名のとおり、個々のタスクではなく長いアニメーション フレームを測定します。長いアニメーション フレームとは、レンダリングの更新が 50 ミリ秒(Long Tasks API のしきい値と同じ)を超えて遅延した場合です。

長いアニメーション フレームは、レンダリングを必要とするタスクの開始から測定されます。長いアニメーション フレーム内の最初のタスクがレンダリングを必要としない場合、長いアニメーション フレームは非レンダリング タスクが完了すると終了し、次のタスクで新しい可能性のある長いアニメーション フレームが開始されます。このようなレンダリングされない長いアニメーション フレームは、50 ミリ秒を超える場合(renderStart 時間が 0 の場合)は、ブロックする可能性のある作業を測定できるように、Long Animation Frames 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 バッファサイズは、long-tasks と同じ 200 に設定されています。

タスクではなくフレームを確認するメリット

タスクの観点ではなくフレームの観点からこれを検討する主な利点は、長いアニメーションを任意の数のタスクで構成し、累積的に長いアニメーション フレームを作成できることです。これは、前述の最後のポイントに対応しています。アニメーション フレームの前に、レンダリングをブロックする小さなタスクが多数あり、その合計が Long Tasks API によってサーフェス表示されなくなる可能性があります。

長いタスクのこの代替ビューのもう 1 つの利点は、フレーム全体のタイミングの内訳を提供できることです。Long Tasks API のように startTimeduration のみを指定せず、LoAF ではフレーム時間のさまざまな部分をより詳細に分類します。

フレームのタイムスタンプと時間

  • startTime: ナビゲーションの開始時間を基準とした長いアニメーション フレームの開始時間。
  • duration: 長いアニメーション フレームの時間(表示時間は含まない)。
  • renderStart: レンダリング サイクルの開始時間(requestAnimationFrame コールバック、スタイルとレイアウトの計算、サイズ変更オブザーバー、インターセクション オブザーバーのコールバックを含む)。
  • styleAndLayoutStart: スタイルとレイアウトの計算に使用される期間の開始。
  • firstUIEventTimestamp: このフレーム中に処理される最初の UI イベント(マウス/キーボードなど)の時間。
  • blockingDuration: アニメーション フレームが入力やその他の優先度の高いタスクの処理をブロックする合計時間(ミリ秒単位)。

blockingDuration の説明

長いアニメーション フレームは、複数のタスクで構成されている場合があります。blockingDuration は、50 ミリ秒を超えるタスクの実行時間の合計(最長のタスク内の最終的なレンダリング時間を含む)です。

たとえば、長いアニメーション フレームが 55 ミリ秒と 65 ミリ秒の 2 つのタスクで構成され、その後に 20 ミリ秒のレンダリングが続く場合、duration は約 140 ミリ秒で、blockingDuration は(55 - 50)+(65 + 20 - 50)= 40 ミリ秒になります。この 140 ミリ秒のアニメーション フレーム中の 40 ミリ秒間、フレームは入力処理からブロックされていると見なされました。

durationblockingDuration のどちらを確認するか

一般的な 60 ヘルツのディスプレイでは、ブラウザは少なくとも 16.66 ミリ秒ごとにフレームのスケジュールを設定するか(更新をスムーズに行うため)、または入力処理などの優先度の高いタスクの後に(レスポンシブな更新を確保するため)スケジュールを試みます。ただし、入力がなく、他の優先度の高いタスクもなく、他のタスクのキューがある場合、ブラウザは通常、各タスクがどの程度分割されていても、16.66 ミリ秒をはるかに超えて現在のフレームを続行します。つまり、ブラウザは常に入力を優先しようとしますが、レンダリングの更新よりもタスクのキューに対応することを選択する場合があります。これは、レンダリングが負荷の高いプロセスであるため、複数のタスクの組み合わせによるレンダリング タスクを処理すると、通常は全体的な作業量が削減されるためです。

したがって、blockingDuration が低いまたはゼロの長いアニメーション フレームでも、入力に応答しているように感じられる必要があります。したがって、長いタスクを分割して blockingDuration を減らすか、完全になくすことが、INP で測定される応答性を改善する鍵となります。

ただし、blockingDuration に関係なく、長いアニメーション フレームが多数ある場合は、UI の更新が遅れていることを示します。そのため、INP で測定される応答性の問題は少ないものの、スムーズさに影響し、スクロールやアニメーションのユーザー インターフェースが遅いと感じられる可能性があります。この領域の問題を把握するには、duration を確認します。ただし、作業を分割して解決することはできず、作業を減らす必要があるため、最適化が難しい場合があります。

フレーム時間

前述のタイムスタンプにより、長いアニメーション フレームを複数のタイミングに分割できます。

タイミング 計算
開始時間 startTime
終了時間 startTime + duration
勤務時間 renderStart ? renderStart - startTime : duration
レンダリング所要時間 renderStart ? (startTime + duration) - renderStart: 0
レンダリング: プレレイアウト時間 styleAndLayoutStart ? styleAndLayoutStart - renderStart : 0
レンダリング: スタイルとレイアウトの時間 styleAndLayoutStart ? (startTime + duration) - styleAndLayoutStart : 0

スクリプトのアトリビューションの改善

long-animation-frame エントリタイプには、長いアニメーション フレームに貢献した各スクリプトのアトリビューション データが含まれています(5 ミリ秒を超えるスクリプトの場合)。

Long Tasks API と同様に、これはアトリビューション エントリの配列で提供されます。各エントリには次の詳細が含まれます。

  • nameEntryType はどちらも script を返します。
  • スクリプトの呼び出し方法を示す意味のある invoker'IMG#id.onload''Window.requestAnimationFrame''Response.json.then' など)。
  • スクリプトのエントリ ポイントの invokerType:
    • user-callback: ウェブ プラットフォーム API から登録された既知のコールバック(setTimeoutrequestAnimationFrame など)。
    • event-listener: プラットフォーム イベントのリスナー(clickloadkeyup など)。
    • resolve-promise: プラットフォームの Promise のハンドラ(例: fetch())。Promise の場合、同じ Promise のすべてのハンドラが 1 つの「スクリプト」として混在することに注意してください)。.
    • 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 Frames API を使用する

Chrome DevTools や Lighthouse などのツールは、問題の検出と再現に役立ちますが、ラボツールであるため、フィールド データでしか得られないユーザー エクスペリエンスの重要な側面を見落とす可能性があります。

Long Animation Frames API は、Long Tasks API では収集できない、ユーザー操作に関する重要なコンテキスト データをフィールドで収集するように設計されています。これにより、他の方法では発見できなかったインタラクティビティに関する問題を特定して再現できます。

長いアニメーション フレーム API のサポート機能の検出

次のコードを使用して、API がサポートされているかどうかをテストできます。

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

Long Animation Frames API の最も明白なユースケースは、Interaction to Next Paint(INP) の問題の診断と修正を支援することです。これが Chrome チームがこの API を開発した主な理由の一つでした。優れた INP とは、インタラクションからフレームが描画されるまで、すべてのインタラクションに 200 ミリ秒以内で応答することです。Long Animation Frames API は、所要時間が 50 ミリ秒以上のフレームをすべて測定するため、問題のある INP には、インタラクションの診断に役立つ LoAF データが含まれているはずです。

次の図に示すように、「INP LoAF」は INP インタラクションを含む LoAF です。

ページ上の長いアニメーション フレームの例。INP LoAF がハイライト表示されています。
1 つのページに多くの LoAF が存在することがあります。そのうちの 1 つは INP インタラクションに関連しています。

場合によっては、INP イベントが 2 つの LoAF にまたがる可能性があります。通常は、前のフレームのレンダリング部分がフレームで開始された後にインタラクションが発生し、イベント ハンドラが次のフレームで処理される場合です。

ページ上の長いアニメーション フレームの例(INP LoAF がハイライト表示されている)。
1 つのページに複数の LoAF が存在し、そのうちの 1 つが INP インタラクションに関連している場合があります。

まれに、3 つ以上の LoAF にまたがることもあります。

INP インタラクションに関連する LoAF(s) データを記録すると、診断に役立つ INP インタラクションに関する詳細情報を取得できます。これは、入力遅延を把握する際に特に役立ちます。そのフレームで実行されていた他のスクリプトを確認できます。

また、イベント ハンドラでこれらの値が再現されない場合は、説明できない処理時間表示遅延を把握することも役に立ちます。ユーザーに対して、ご自身のテストに含まれていない他のスクリプトが実行されている可能性があります。

INP エントリを関連する LoAF エントリにリンクする直接の API はありませんが、各エントリの開始時間と終了時間を比較することで、コード内でリンクすることは可能です(WhyNp サンプル スクリプトをご覧ください)。web-vitals ライブラリには、v4 の INP アトリビューション インターフェースの longAnimationFramesEntries プロパティに含まれる、重複するすべての LoAF が含まれています。

LoAF エントリをリンクしたら、INP アトリビューションを含む情報を含めることができます。scripts オブジェクトには、それらのフレームで他に実行されていたものを確認できるため、最も重要な情報が含まれています。そのデータをアナリティクス サービスに返すことで、インタラクションが遅い理由をより深く理解できます。

INP インタラクションの LoAF をレポートすると、ページで最も緊急性の高いインタラクションの問題を特定できます。ユーザーによってページの操作は異なるため、十分な量の INP アトリビューション データが収集されると、INP アトリビューション データにさまざまな問題が含まれる可能性があります。これにより、スクリプトをボリューム別に並べ替えて、INP の遅延と関連するスクリプトを確認できます。

長いアニメーション データをアナリティクス エンドポイントに報告する

INP LoAF のみを確認するデメリットの一つは、将来の INP の問題の原因となる可能性のある他の改善領域を見逃す可能性があることです。そのため、INP の問題を修正して大幅な改善を期待しても、次に遅いインタラクションの時間がわずかに短縮されただけで、INP の改善があまり進まない、という状況に陥る可能性があります。

したがって、INP LoAF だけでなく、ページのライフタイム全体のすべての LoAF を考慮することをおすすめします。

LoAF が多いページ。一部の LoAF は、INP インタラクション以外でもインタラクション中に発生します。
すべての LoAF を確認すると、将来の INP の問題を特定できます。

ただし、各 LoAF エントリにはかなりのデータが含まれているため、分析対象を一部の LoAF のみに制限することをおすすめします。また、長いアニメーション フレームのエントリは非常に大きい場合があるため、エントリから分析に送信するデータをデベロッパーが決定する必要があります。たとえば、エントリの概要時間やスクリプト名、または必要と判断されるその他のコンテキスト データの最小セットなどです。

長いアニメーションのフレームデータの量を減らすために推奨されるパターンは次のとおりです。

どのパターンが最適かは、最適化の過程のどの段階にあるか、またアニメーション フレームが長いかどうかによって決まります。レスポンシブ デザインを最適化したことがないサイトでは、LoAF が多数存在する可能性があります。そのような場合は、インタラクションが発生した LoAF のみに絞り込むか、高いしきい値を設定する、または最も問題のある LoAF のみを確認することをおすすめします。

一般的な応答性の問題を解決したら、インタラクションや長時間のブロックのみに制限せず、しきい値を下げて範囲を広げることができます。

インタラクションのある長いアニメーション フレームをモニタリングする

INP の長いアニメーション フレーム以外の分析情報を得るには、blockingDuration が高いインタラクション(firstUIEventTimestamp 値の有無で検出可能)を含むすべての LoAF を確認します。

また、2 つの関連付けを試みるよりも、INP LoAF をモニタリングする簡単な方法でもあります。ほとんどの場合、これには特定の訪問の INP LoAF が含まれますが、まれに、それ以外のユーザーの INP インタラクションであるため、修正が重要な長いインタラクションが引き続き表示されます。

次のコードは、フレーム中にインタラクションが発生したすべての LoAF エントリを、blockingDuration が 100 ミリ秒を超えるものとしてロギングします。ここでは、INP の「良好」なしきい値である 200 ミリ秒より小さいため、100 を選択しました。必要に応じて、この値を大きくしたり小さくしたりできます。

const REPORTING_THRESHOLD_MS = 100;

const observer = new PerformanceObserver(list => {
  for (const entry of list.getEntries()) {
    if (entry.blockingDuration > 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 });

長いアニメーション フレームと長いブロック時間を確認する

インタラクションのある長いアニメーション フレームすべてを確認する代わりに、ブロック時間が長い長いアニメーション フレームすべてを確認することをおすすめします。これらの長いアニメーション フレーム中にユーザーが操作を行った場合、INP の問題が発生する可能性があります。

次のコードは、フレーム中にインタラクションが発生した、ブロック時間 100 ミリ秒を超えるすべての LoAF エントリをロギングします。ここで 100 が選択されているのは、200 ミリ秒の「正常な」INP しきい値よりも小さいため、問題の可能性があるフレームを特定しつつ、報告される長いアニメーション フレームの量を最小限に抑えることができるためです。必要に応じて、より高い値または低い値を選択できます。

const REPORTING_THRESHOLD_MS = 100;

const observer = new PerformanceObserver(list => {
  for (const entry of list.getEntries()) {
    if (entry.blockingDuration > 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 });

重要な UI の更新中に長いアニメーション フレームを監視してスムーズさを改善

前述のように、ブロック時間が長い長いアニメーション フレームを確認することで、入力の応答性に対処しやすくなります。ただし、スムーズにするには、長い duration を持つ長いアニメーション フレームすべてを確認する必要があります。

ノイズが非常に多い場合は、次のようなパターンで測定ポイントをキーポイントに制限することをおすすめします。

const REPORTING_THRESHOLD_MS = 100;

const observer = new PerformanceObserver(list => {
  if (measureImportantUIupdate) {
    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 });

async function doUIUpdatesWithMeasurements() {
  measureImportantUIupdate = true;
  await doUIUpdates();
  measureImportantUIupdate = false;
}

長いアニメーション フレームで最もパフォーマンスが低下している部分を確認する

サイトによっては、ビーコンで送信する必要があるデータ量を減らすために、しきい値を設定するのではなく、最も長いアニメーション フレーム(またはフレーム)に関するデータを収集することもできます。そのため、ページで長いアニメーション フレームがいくつ発生しても、最も長いアニメーション フレーム(5 フレーム、10 フレームなど、絶対に必要なフレーム数)のデータのみがビーコンで送信されます。

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 });

これらの戦略は組み合わせることもできます。100 ミリ秒を超えるインタラクションが含まれる 10 件の最悪の LoAF のみに注目します。

適切なタイミング(visibilitychange イベントが理想的)で、アナリティクスにビーコンを返します。ローカルテストでは、定期的に console.table を使用できます。

console.table(longestBlockingLoAFs);

長いアニメーション フレームの一般的なパターンを特定する

別の方法として、長いアニメーション フレーム エントリで最も多く出現する一般的なスクリプトを確認することもできます。データはスクリプトと文字位置レベルで報告され、違反を繰り返すユーザーを特定できます。

これは、パフォーマンスの問題を引き起こしているテーマやプラグインを複数のサイトで特定できるカスタマイズ可能なプラットフォームで特に有効な場合があります。

長いアニメーション フレーム内の一般的なスクリプト(またはサードパーティ オリジン)の実行時間を合計してレポートすることで、サイトまたはサイトのコレクション全体で長いアニメーション フレームの発生に共通して影響している要素を特定できます。たとえば、URL を確認するには:

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 DevTools などの一部のツールは、低レベルのトレース詳細を使用してこのデータの大部分を収集できましたが、この高レベルの API を使用すると、他のツールもこのデータにアクセスできるようになります。

DevTools で長いアニメーション フレームのデータを表示する

performance.measure() API を使用して、長いアニメーション フレームを DevTools に表示できます。このフレームは、パフォーマンス トレースの DevTools のユーザー タイミング トラックに表示され、パフォーマンス改善に重点を置くべき場所を示します。DevTools Extensibility API を使用すると、これらのイベントを独自のトラックに表示することもできます。

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    performance.measure('LoAF', {
      start: entry.startTime,
      end: entry.startTime + entry.duration,
      detail: {
        devtools: {
          dataType: "track-entry",
          track: "Long animation frames",
          trackGroup: "Performance Timeline",
          color: "tertiary-dark",
          tooltipText: 'LoAF'
        }
      }
    });
  }
});

observer.observe({ type: 'long-animation-frame', buffered: true });
メインの炎グラフと比較できる長いアニメーション フレームのデータが表示されたカスタム トラックを含む DevTools パフォーマンス パネルのトレース。
DevTools で長いアニメーション フレームデータを表示する

長期的には、長いアニメーション フレームは DevTools 自体に組み込まれる可能性がありますが、それまでの間は、前のコード スニペットを使用して表示できます。

上の図の最初のエントリは、ブラウザが複数のタスクを、タスク間でレンダリングするのではなく、同じ長いアニメーション フレームでまとめて処理した場所を示しています。前述のように、優先度の高い入力タスクは存在しないものの、タスクのキューがある場合に発生することがあります。最初の長いタスクには、完了するレンダリング アップデートがいくつかあります(完了しないと、現在の長いアニメーション フレームはその後でリセットされ、次のタスクで新しいフレームが開始されます)。しかし、ブラウザは、そのレンダリングをすぐに実行するのではなく、いくつかの追加タスクを処理してから、長いレンダリング タスクを実行し、長いアニメーション フレームを終了しました。これは、長いタスクだけでなく、DevTools で長いアニメーション フレームを確認して、レンダリングの遅延を特定する方法の有用性を示しています。

他のデベロッパー ツールで長いアニメーション フレームのデータを使用する

ウェブに関する主な指標拡張機能が、ロギング概要のデバッグ情報に値を表示して、パフォーマンスの問題を診断できるようになりました。

また、各 INP コールバックと各インタラクションの長いアニメーション フレームデータも表示されるようになります。

Web Vitals 拡張機能のコンソール ロギング。
Web Vitals 拡張機能のコンソールのロギングに LoAF データが表示されます。

自動テストツールで長いアニメーション フレームのデータを使用する

同様に、CI/CD パイプラインの自動テストツールは、さまざまなテストスイートを実行しながら長いアニメーション フレームを測定することで、潜在的なパフォーマンスの問題の詳細を表示できます。

よくある質問

この API に関するよくある質問を以下に示します。

Long Tasks API を拡張または反復処理しないのはなぜですか?

これは、潜在的な応答性の問題に関する同様の測定値(最終的には異なる測定値)を報告する別の方法です。既存のユースケースが中断しないように、既存の Long Tasks API を使用しているサイトが機能し続けられるようにすることが重要です。

Long Tasks API は LoAF のいくつかの機能(より優れたアトリビューション モデルなど)の恩恵を受けることができますが、タスクではなくフレームに焦点を当てることには多くのメリットがあり、既存の Long Tasks API とは根本的に異なる API になると考えています。

スクリプト エントリがないのはなぜですか?

これは、長いアニメーション フレームが JavaScipt ではなく、レンダリング作業の負荷が高いことが原因であることを示している可能性があります。

また、長いアニメーション フレームが JavaScript によるものであるにもかかわらず、前述のさまざまなプライバシー上の理由(主に、JavaScript がページ所有のものではない)によりスクリプトの帰属を提供できない場合にも発生することがあります。

スクリプト エントリはあるのに、ソース情報がないか、制限されているのはなぜですか?

これには、参照する適切なソースがないなど、さまざまな理由が考えられます。

スクリプト情報は no-cors cross-origin スクリプトでも制限されますが、<script> 呼び出しに crossOrigin = "anonymous" を追加して CORS を使用してスクリプトを取得することで解決できます。

たとえば、ページに追加するデフォルトの Google タグ マネージャー スクリプトは次のようになります。

<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXXX');</script>
<!-- End Google Tag Manager -->

j.crossOrigin = "anonymous" を追加して拡張し、完全なアトリビューションの詳細を GTM に提供できるようにします。

これは Long Tasks API に代わるものですか?

Long Animation Frames API は、長いタスクの測定に適した、より優れた API ですが、現時点では Long Tasks API を非推奨にする予定はありません。

フィードバックを求めている

フィードバックは GitHub の問題リストでお送りください。また、Chrome の API 実装に関するバグについては Chrome の Issue Tracker にお寄せください。

まとめ

Long Animation Frames API は、以前の Long Tasks API よりも多くのメリットが期待される、魅力的な新しい API です。

INP で測定される応答性の問題に対処するための重要なツールであることが実証されています。INP は最適化が難しい指標です。この API は、デベロッパーが問題を簡単に特定して対処できるように、Chrome チームが検討している方法の一つです。

ただし、Long Animation Frames API の対象は INP だけではありません。ウェブサイトのユーザー エクスペリエンスの全体的なスムーズさに影響する可能性のある、更新の遅延の他の原因を特定するのにも役立ちます。

謝辞

サムネイル画像: UnsplashHenry Be による