API Frame Animasi Panjang

Long Animation Frames API (LoAF - diucapkan Lo-Af) adalah update dari Long Tasks API untuk memberikan pemahaman yang lebih baik tentang update antarmuka pengguna (UI) yang lambat. Hal ini dapat berguna untuk mengidentifikasi frame animasi lambat yang mungkin memengaruhi metrik Core Web Vital Interaction to Next Paint (INP) yang mengukur responsivitas, atau untuk mengidentifikasi jank pada UI lainnya yang memengaruhi kehalusan.

Status API

Dukungan Browser

  • 123
  • x
  • x
  • x

Setelah uji coba origin dari Chrome 116 ke Chrome 122, LoAF API telah dikirim dari Chrome 123.

Long Tasks API

Dukungan Browser

  • 58
  • 79
  • x
  • x

Sumber

Long Animation Frames API adalah alternatif dari Long Tasks API yang telah tersedia di Chrome selama beberapa waktu (sejak Chrome 58). Seperti namanya, Long Task API memungkinkan Anda memantau tugas yang panjang, yaitu tugas yang menempati thread utama selama 50 milidetik atau lebih. Tugas yang panjang dapat dipantau menggunakan antarmuka PerformanceLongTaskTiming, dengan PeformanceObserver:

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

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

Tugas yang berjalan lama cenderung menyebabkan masalah respons. Jika pengguna mencoba berinteraksi dengan halaman—misalnya, mengklik tombol atau membuka menu—tetapi thread utama sudah menangani tugas yang panjang, interaksi pengguna tertunda untuk menunggu tugas tersebut diselesaikan.

Untuk meningkatkan responsivitas, sering kali disarankan untuk memecah tugas yang panjang. Jika setiap tugas yang panjang dipecah menjadi serangkaian tugas yang lebih kecil, mungkin akan memungkinkan tugas yang lebih penting dijalankan di antara tugas-tugas tersebut untuk menghindari penundaan yang signifikan dalam merespons interaksi.

Jadi, saat mencoba meningkatkan responsivitas, upaya pertama sering kali adalah menjalankan pelacakan performa dan melihat tugas yang berjalan lama. Hal ini bisa dilakukan melalui alat audit berbasis lab seperti Lighthouse (yang memiliki audit Hindari tugas thread utama yang panjang), atau dengan melihat tugas yang berjalan lama di Chrome DevTools.

Pengujian berbasis lab sering kali menjadi titik awal yang buruk untuk mengidentifikasi masalah responsivitas, karena alat ini mungkin tidak menyertakan interaksi—jika ada, alat ini hanya merupakan sebagian kecil dari kemungkinan interaksi. Idealnya, Anda akan mengukur penyebab interaksi yang lambat di lapangan.

Kekurangan pada Long Tasks API

Mengukur tugas yang panjang di lapangan menggunakan Performance Observer hanya bermanfaat. Kenyataannya, visualisasi itu tidak memberikan informasi sebanyak itu di luar fakta bahwa tugas yang panjang terjadi, dan berapa lama waktu yang dibutuhkan.

Alat Real User Monitoring (RUM) sering menggunakan ini untuk membuat tren jumlah atau durasi tugas yang panjang atau mengidentifikasi halaman tempat tugas tersebut dilakukan—tetapi tanpa detail yang mendasari tentang penyebab tugas yang panjang, ini hanyalah penggunaan terbatas. Long Tasks API hanya memiliki model atribusi dasar, yang paling baik hanya memberi tahu Anda container tempat tugas panjang terjadi (dokumen level teratas atau <iframe>), tetapi bukan skrip atau fungsi yang memanggilnya, seperti yang ditunjukkan oleh entri biasa:

{
  "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 juga merupakan tampilan yang tidak lengkap, karena juga dapat mengecualikan beberapa tugas penting. Beberapa update—seperti rendering—terjadi dalam tugas terpisah yang idealnya harus disertakan bersama dengan eksekusi sebelumnya yang menyebabkan update tersebut mengukur "total pekerjaan" secara akurat untuk interaksi tersebut. Untuk detail selengkapnya tentang batasan mengandalkan tugas, lihat bagian "Jika tugas yang berjalan lama tidak selesai" pada penjelasan.

Masalah terakhir adalah mengukur tugas yang panjang hanya melaporkan setiap tugas yang membutuhkan waktu lebih dari batas 50 milidetik. Frame animasi dapat terdiri dari beberapa tugas di bawah batas 50 milidetik ini, namun secara kolektif masih memblokir kemampuan browser untuk merender.

Long Animation Frames API

Dukungan Browser

  • 123
  • x
  • x
  • x

Long Animation Frames API (LoAF) adalah API baru yang berupaya mengatasi beberapa kekurangan Long Tasks API untuk memungkinkan developer mendapatkan lebih banyak hasil analisis yang bisa ditindaklanjuti guna membantu mengatasi masalah responsivitas dan meningkatkan INP.

Respons yang baik berarti halaman merespons dengan cepat interaksi yang dilakukan dengan halaman tersebut. Hal ini melibatkan kemampuan melukiskan pembaruan apa pun yang diperlukan oleh pengguna secara tepat waktu, dan menghindari pemblokiran pembaruan ini. Untuk INP, sebaiknya respons dalam waktu 200 milidetik atau kurang, tetapi untuk update lainnya (misalnya, animasi) meskipun mungkin terlalu lama.

Long Animation Frames API adalah pendekatan alternatif untuk mengukur pekerjaan pemblokiran. Alih-alih mengukur tugas individual, Long Animation Frames API—seperti namanya—mengukur frame animasi panjang. Frame animasi yang panjang adalah saat update rendering tertunda lebih dari 50 milidetik (sama dengan nilai minimum untuk Long Tasks API).

Frame animasi panjang dapat diamati dengan cara yang sama selama tugas panjang dengan PerformanceObserver, tetapi melihat jenis long-animation-frame sebagai gantinya:

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

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

Frame animasi panjang sebelumnya juga dapat dikueri dari Linimasa Performa seperti berikut:

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

Namun, ada maxBufferSize untuk entri performa setelah entri yang lebih baru dihapus, sehingga pendekatan PerformanceObserver adalah pendekatan yang direkomendasikan. Ukuran buffer long-animation-frame disetel ke 200, sama seperti ukuran long-tasks.

Keuntungan melihat {i>frame<i} daripada tugas

Keuntungan utama melihat ini dari perspektif frame daripada perspektif tugas, adalah bahwa animasi panjang dapat terdiri dari sejumlah tugas yang secara kumulatif menghasilkan frame animasi panjang. Bagian ini membahas poin terakhir yang disebutkan sebelumnya, di mana jumlah banyak tugas yang lebih kecil dan memblokir perenderan sebelum bingkai animasi mungkin tidak dimunculkan oleh Long Tasks API.

Keuntungan lebih lanjut dari tampilan alternatif ini pada tugas yang berjalan lama adalah kemampuan untuk memberikan perincian waktu seluruh frame. Bukan hanya menyertakan startTime dan duration, seperti Long Tasks API, LoAF menyertakan perincian yang jauh lebih mendetail tentang berbagai bagian durasi frame termasuk:

  • startTime: waktu mulai frame animasi panjang relatif terhadap waktu mulai navigasi.
  • duration: durasi frame animasi panjang (tidak termasuk waktu presentasi).
  • renderStart: waktu mulai siklus rendering, yang mencakup callback requestAnimationFrame, penghitungan gaya dan tata letak, callback ubah ukuran observer, dan callback observer persimpangan.
  • styleAndLayoutStart: awal jangka waktu yang dihabiskan dalam penghitungan gaya dan tata letak.
  • firstUIEventTimestamp: waktu peristiwa UI pertama (mouse/keyboard dan sebagainya) yang akan ditangani selama frame ini berlangsung.
  • blockingDuration: durasi dalam milidetik saat frame animasi diblokir.

Stempel waktu ini memungkinkan frame animasi panjang dibagi menjadi pengaturan waktu:

Waktu Perhitungan
Waktu Mulai startTime
Waktu Berakhir startTime + duration
Durasi kerja renderStart ? renderStart - startTime : duration
Durasi render renderStart ? (startTime + duration) - renderStart: 0
Render: Durasi pra-tata letak styleAndLayoutStart ? styleAndLayoutStart - renderStart : 0
Render: Durasi Gaya dan Tata Letak styleAndLayoutStart ? (startTime + duration) - styleAndLayoutStart : 0

Untuk detail selengkapnya tentang setiap pengaturan waktu ini, lihat penjelasan, yang memberikan detail terperinci tentang aktivitas mana yang berkontribusi pada frame animasi yang panjang.

Atribusi yang lebih baik

Jenis entri long-animation-frame mencakup data atribusi yang lebih baik dari setiap skrip yang berkontribusi pada frame animasi yang panjang.

Mirip dengan Long Tasks API, API ini akan disediakan dalam array entri atribusi, yang masing-masing detailnya:

  • name dan EntryType akan menampilkan script.
  • invoker yang bermakna, yang menunjukkan cara skrip dipanggil (misalnya, 'IMG#id.onload', 'Window.requestAnimationFrame', atau 'Response.json.then').
  • invokerType titik entri skrip:
    • user-callback: Callback yang diketahui yang didaftarkan dari API platform web (misalnya, setTimeout, requestAnimationFrame).
    • event-listener: Pemroses ke peristiwa platform (misalnya, click, load, keyup).
    • resolve-promise: Pengendali promise platform (misalnya, fetch(). Perlu diperhatikan bahwa dalam kasus promise, semua pengendali promise yang sama dicampur menjadi satu "skrip").
    • reject-promise: Sesuai resolve-promise, tetapi untuk penolakan.
    • classic-script: Evaluasi skrip (misalnya <script> atau import())
    • module-script: Sama seperti classic-script, tetapi untuk skrip modul.
  • Data pengaturan waktu terpisah untuk skrip tersebut:
    • startTime: Waktu fungsi entri dipanggil.
    • duration: Durasi antara startTime dan saat antrean tugas mikro berikutnya selesai diproses.
    • executionStart: Waktu setelah kompilasi.
    • forcedStyleAndLayoutDuration: Total waktu yang dihabiskan untuk memproses tata letak/gaya paksa dalam fungsi ini (lihat thrashing).
    • pauseDuration: Total waktu yang dihabiskan dalam operasi sinkron "menjeda" (pemberitahuan, XHR sinkron).
  • Detail sumber skrip:
    • sourceURL: Nama resource skrip jika tersedia (atau kosong jika tidak ditemukan).
    • sourceFunctionName: Nama fungsi skrip jika tersedia (atau kosong jika tidak ditemukan).
    • sourceCharPosition: Posisi karakter skrip jika tersedia (atau -1 jika tidak ditemukan).
  • windowAttribution: Container (dokumen tingkat atas, atau <iframe>) tempat frame animasi panjang terjadi.
  • window: Referensi ke jendela origin yang sama.

Jika disediakan, entri sumber memungkinkan developer mengetahui dengan tepat cara setiap skrip di frame animasi panjang dipanggil, hingga ke posisi karakter dalam skrip panggilan. Ini memberikan lokasi yang tepat dalam resource JavaScript yang menghasilkan frame animasi panjang.

Contoh entri performa long-animation-frame

Contoh entri performa long-animation-frame lengkap yang berisi satu skrip adalah:

{
  "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
}

Seperti yang terlihat, hal ini memberikan jumlah data yang belum pernah ada sebelumnya bagi situs untuk memahami penyebab update rendering yang lambat.

Mengaktifkan Long Animation Frames API

Long Animation Frames API diaktifkan secara default dari Chrome 123.

Menggunakan Long Animation Frames API pada kolom

Alat seperti Lighthouse—meskipun berguna untuk menemukan dan mereproduksi masalah—adalah alat lab yang mungkin melewatkan aspek penting pengalaman pengguna yang hanya dapat diberikan oleh data lapangan. Long Animation Frames API dapat digunakan dalam kolom untuk mengumpulkan data kontekstual penting untuk interaksi pengguna yang tidak dapat dilakukan oleh Long Tasks API. Hal ini dapat membantu Anda memunculkan dan memunculkan kembali masalah dengan interaktivitas yang mungkin tidak Anda temukan.

Beberapa strategi yang disarankan dicantumkan berikutnya, tetapi tim Chrome ingin mendengar masukan tentang API ini serta bagaimana developer dan penyedia RUM akan memandang diri mereka sendiri menggunakan informasi yang disediakan oleh API ini.

Fitur yang mendeteksi dukungan Long Animation Frames API

Anda dapat menggunakan kode berikut untuk menguji apakah API didukung:

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

Alternatif berikut dapat digunakan dalam hal ini, sementara frame animasi yang panjang belum didukung secara default dan berada dalam status transisi ini:

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

Melaporkan data animasi panjang kembali ke endpoint analisis

Seperti yang ditunjukkan, entri performa LoAF menyertakan informasi berharga. Salah satu strateginya adalah memantau semua LoAF dan beacon yang berada di atas ambang batas tertentu kembali ke titik akhir analisis untuk dianalisis nanti:

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

Karena entri bingkai animasi yang panjang bisa sangat besar, pengembang harus memutuskan data apa dari entri yang harus dikirim ke analitik. Misalnya, waktu ringkasan entri dan mungkin nama skrip, atau beberapa kumpulan minimum data kontekstual lainnya yang mungkin dianggap perlu.

Mengamati frame animasi panjang terburuk

Situs mungkin ingin mengumpulkan data pada frame animasi (atau frame) terpanjang, untuk mengurangi volume data yang perlu beacon. Jadi, tidak peduli berapa banyak {i>frame<i} animasi yang panjang, hanya data untuk {i>frame<i} yang terburuk, lima, atau berapa pun {i>frame<i} animasi yang panjang yang benar-benar diperlukan yang harus diaconed kembali.

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

Pada waktu yang tepat (idealnya pada peristiwa visibilitychange) beacon kembali ke Analytics. Untuk pengujian lokal, Anda dapat menggunakan console.table secara berkala:

console.table(longestBlockingLoAFs);

Menautkan ke interaksi INP terpanjang

Sebagai kepanjangan dari pengamatan LoAF terburuk, frame LoAF yang sesuai dengan entri INP dapat digunakan sebagai data atribusi untuk memberikan detail lebih lanjut tentang cara meningkatkan INP.

Saat ini tidak ada API langsung untuk menautkan entri INP dengan entri atau entri LoAF terkait, meskipun hal ini dapat dilakukan dalam kode dengan membandingkan waktu mulai dan waktu berakhir masing-masing (lihat contoh skrip ini).

Melaporkan frame animasi panjang dengan interaksi

Pendekatan alternatif yang memerlukan lebih sedikit kode adalah dengan selalu mengirim entri LoAF terbesar (atau X terbesar) ketika interaksi terjadi selama frame (yang dapat dideteksi dengan adanya nilai firstUIEventTimestamp). Pada umumnya, hal ini akan mencakup interaksi INP untuk kunjungan tertentu, dan dalam kasus yang jarang terjadi, jika tidak, masih menampilkan interaksi panjang yang penting untuk diperbaiki, karena mungkin merupakan interaksi INP bagi pengguna lain.

Kode berikut mencatat semua entri LoAF yang lebih besar dari 150 milidetik saat interaksi terjadi selama frame. 150 dipilih di sini karena sedikit kurang dari ambang batas INP "baik" 200 milidetik. Anda dapat memilih nilai yang lebih tinggi atau lebih rendah tergantung kebutuhan Anda.

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

Mengidentifikasi pola umum dalam {i>frame<i} animasi yang panjang

Strategi alternatifnya adalah dengan melihat skrip umum yang paling banyak muncul dalam entri frame animasi yang panjang. Data dapat dilaporkan kembali pada tingkat posisi skrip dan/atau karakter untuk mengidentifikasi pelanggar berulang.

Ini terutama bekerja dengan baik untuk platform yang dapat disesuaikan ketika tema atau plugin yang menyebabkan masalah kinerja dapat lebih mudah diidentifikasi di sejumlah situs.

Waktu eksekusi skrip umum—atau asal pihak ketiga—dalam frame animasi yang panjang dapat diringkas dan dilaporkan kembali untuk mengidentifikasi kontributor umum bagi frame animasi panjang di seluruh situs atau kumpulan situs. Misalnya untuk melihat 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});

Dan contoh dari output ini adalah:

(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

Menggunakan Long Animation Frames API pada alat

API tersebut juga dapat mengizinkan alat developer tambahan untuk proses debug lokal. Meskipun beberapa fitur seperti Lighthouse dan Chrome DevTools telah dapat mengumpulkan banyak data ini menggunakan detail pelacakan tingkat rendah, memiliki API level yang lebih tinggi ini dapat memungkinkan alat lain untuk mengakses data ini.

Memunculkan data frame animasi panjang di DevTools

Anda dapat menampilkan frame animasi panjang di DevTools menggunakan performance.measure() API, yang kemudian ditampilkan di jalur waktu pengguna DevTools dalam rekaman aktivitas performa untuk menunjukkan area yang harus difokuskan dalam upaya peningkatan performa:

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

Jika terbukti berguna dalam jangka panjang, API ini kemungkinan akan disertakan ke dalam DevTools itu sendiri, tetapi cuplikan kode sebelumnya memungkinkannya untuk ditampilkan di sana.

Menggunakan data frame animasi panjang di alat developer lainnya

Ekstensi Data Web telah menunjukkan nilai dalam logging informasi debug ringkasan untuk mendiagnosis masalah performa. Sekarang API telah diluncurkan, alat seperti itu dapat lebih mudah menampilkan data untuk membantu developer mengetahui di mana mereka harus memusatkan upaya mereka. Kami juga berencana untuk menambahkannya ke library JavaScript web vitals di versi 4.

Menggunakan data frame animasi panjang dalam alat pengujian otomatis

Demikian pula, alat pengujian otomatis, mungkin di pipeline CI/CD, dapat menampilkan detail tentang potensi masalah performa dengan mengukur frame animasi yang panjang saat menjalankan berbagai rangkaian pengujian.

FAQ

Beberapa pertanyaan umum (FAQ) tentang API ini mencakup:

Mengapa tidak memperluas atau melakukan iterasi saja di Long Tasks API?

Ini adalah tampilan alternatif dalam melaporkan pengukuran yang mirip—tetapi pada akhirnya berbeda—untuk potensi masalah responsivitas. Pastikan situs yang mengandalkan Long Tasks API yang sudah ada akan terus berfungsi agar tidak mengganggu kasus penggunaan yang sudah ada.

Meskipun Long Tasks API dapat memanfaatkan beberapa fitur LoAF (seperti model atribusi yang lebih baik), kami percaya bahwa berfokus pada bingkai, bukan tugas, menawarkan banyak manfaat yang menjadikan API ini sangat berbeda dengan Long Tasks API yang sudah ada.

Apakah ini akan menggantikan Long Tasks API?

Meskipun kami yakin bahwa Long Animation Frames API adalah API yang lebih baik dan lebih lengkap untuk mengukur tugas yang panjang, saat ini, tidak ada rencana untuk menghentikan penggunaan Long Tasks API.

Masukan diinginkan

Masukan dapat diberikan di daftar Masalah GitHub, atau bug dalam implementasi API Chrome dapat dilaporkan di Issue Tracker Chrome.

Kesimpulan

Long Animation Frames API adalah API baru yang menarik dengan banyak potensi keunggulan dibandingkan Long Tasks API sebelumnya.

Eksperimen ini terbukti menjadi alat utama untuk mengatasi masalah responsivitas seperti yang diukur oleh INP. INP adalah metrik yang menantang untuk dioptimalkan dan API ini merupakan salah satu cara tim Chrome berupaya mempermudah identifikasi dan penanganan masalah bagi developer.

Namun, cakupan Long Animation Frames API tidak hanya terbatas pada INP, tetapi juga dapat membantu mengidentifikasi penyebab lain update yang lambat yang dapat memengaruhi kelancaran pengalaman pengguna situs secara keseluruhan.

Ucapan terima kasih

Gambar thumbnail oleh Henry Be di Unsplash.