Menemukan interaksi lambat di lapangan

Pelajari cara menemukan interaksi lambat di data kolom situs, sehingga Anda dapat menemukan peluang untuk meningkatkan Interaksi ke Next Paint.

Data kolom adalah data yang menunjukkan pengalaman pengguna sebenarnya terhadap situs Anda. Laporan ini mengedepankan masalah yang tidak dapat Anda temukan di data lab saja. Jika Interaction to Next Paint (INP) menjadi perhatian, data lapangan sangat penting dalam mengidentifikasi interaksi yang lambat, dan memberikan petunjuk penting untuk membantu Anda memperbaikinya.

Dalam panduan ini, Anda akan mempelajari cara menilai INP situs dengan cepat menggunakan data kolom dari Laporan Pengalaman Pengguna Chrome (CrUX) untuk melihat apakah situs Anda mengalami masalah dengan INP. Selanjutnya, Anda akan mempelajari cara menggunakan build atribusi library JavaScript web-vitals—dan insight baru yang disediakannya dari Long Animation Frames API (LoAF)—untuk mengumpulkan dan menafsirkan data kolom bagi interaksi lambat di situs Anda.

Mulai dengan CrUX untuk mengevaluasi INP situs Anda

Jika Anda tidak mengumpulkan data lapangan dari pengguna situs Anda, CrUX mungkin bisa menjadi titik awal yang baik. CrUX mengumpulkan data kolom dari pengguna Chrome sebenarnya yang telah memilih untuk mengirim data telemetri.

Data CrUX muncul di sejumlah area berbeda, dan bergantung pada cakupan informasi yang Anda cari. CrUX dapat memberikan data tentang INP dan Core Web Vitals lainnya untuk:

  • Setiap halaman dan seluruh asal menggunakan PageSpeed Insights.
  • Jenis halaman. Misalnya, banyak situs e-commerce memiliki jenis Halaman Detail Produk dan Halaman Listingan Produk. Anda dapat memperoleh data CrUX untuk jenis halaman unik di Search Console.

Sebagai titik awal, Anda dapat memasukkan URL situs di PageSpeed Insights. Setelah Anda memasukkan URL, data kolom URL tersebut—jika tersedia—akan ditampilkan untuk beberapa metrik, termasuk INP. Anda juga dapat menggunakan tombol beralih guna memeriksa nilai INP untuk dimensi seluler dan desktop.

Data kolom seperti yang ditunjukkan oleh CrUX di PageSpeed Insights, yang menampilkan LCP, INP, CLS di tiga Core Web Vitals, dan TTFB, FCP sebagai metrik diagnostik, dan FID sebagai metrik Core Web Vitals yang tidak digunakan lagi.
Pembacaan data CrUX seperti yang terlihat di PageSpeed Insights. Dalam contoh ini, INP halaman web tertentu memerlukan peningkatan.

Data ini berguna karena memberi tahu Anda apakah Anda memiliki masalah. Namun, hal yang tidak dapat dilakukan CrUX adalah memberi tahu Anda apa yang menyebabkan masalah. Ada banyak solusi Real User Monitoring (RUM) yang tersedia yang akan membantu Anda mengumpulkan data kolom sendiri dari pengguna situs untuk menjawabnya, dan salah satu opsinya adalah mengumpulkan data kolom itu sendiri menggunakan library JavaScript web-vitals.

Mengumpulkan data kolom dengan library JavaScript web-vitals

Library JavaScript web-vitals adalah skrip yang dapat Anda muat di situs untuk mengumpulkan data kolom dari pengguna situs. Anda dapat menggunakannya untuk mencatat sejumlah metrik, termasuk INP di browser yang mendukungnya.

Dukungan Browser

  • 96
  • 96
  • x
  • x

Sumber

Build standar library web-vitals dapat digunakan untuk mendapatkan data INP dasar dari pengguna di lapangan:

import {onINP} from 'web-vitals';

onINP(({name, value, rating}) => {
  console.log(name);    // 'INP'
  console.log(value);   // 512
  console.log(rating);  // 'poor'
});

Untuk menganalisis data kolom dari pengguna, Anda dapat mengirimkan data ini ke suatu tempat:

import {onINP} from 'web-vitals';

onINP(({name, value, rating}) => {
  // Prepare JSON to be sent for collection. Note that
  // you can add anything else you'd want to collect here:
  const body = JSON.stringify({name, value, rating});

  // Use `sendBeacon` to send data to an analytics endpoint.
  // For Google Analytics, see https://github.com/GoogleChrome/web-vitals#send-the-results-to-google-analytics.
  navigator.sendBeacon('/analytics', body);
});

Namun, data ini sendiri tidak memberi tahu Anda lebih banyak dari yang dikatakan CrUX. Di sinilah build atribusi library web-vitals berperan.

Melangkah lebih jauh dengan build atribusi library web-vitals

Pembuatan atribusi library web-vitals menampilkan data tambahan yang bisa Anda dapatkan dari pengguna di kolom untuk membantu Anda memecahkan masalah interaksi bermasalah yang memengaruhi INP situs Anda dengan lebih baik. Data ini dapat diakses melalui objek attribution yang ditampilkan di metode onINP() library:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, rating, attribution}) => {
  console.log(name);         // 'INP'
  console.log(value);        // 512
  console.log(rating);       // 'poor'
  console.dir(attribution);  // Attribution data
});
Cara log konsol dari library web-vitals muncul. Konsol dalam contoh ini menampilkan nama metrik (INP), nilai INP (56), dengan nilai tersebut berada dalam nilai minimum INP (baik), dan berbagai bit informasi yang ditampilkan dalam objek atribusi, termasuk entri dari The Long Animation Frame API.
Cara data dari library web-vitals muncul di konsol.

Selain INP halaman itu sendiri, build atribusi menyediakan banyak data yang dapat Anda gunakan untuk membantu memahami alasan interaksi lambat, termasuk bagian interaksi mana yang harus Anda fokuskan. Hal ini dapat membantu Anda menjawab pertanyaan penting seperti:

  • "Apakah pengguna berinteraksi dengan halaman saat sedang dimuat?"
  • "Apakah pengendali peristiwa interaksi berjalan dalam waktu yang lama?"
  • "Apakah kode pengendali peristiwa interaksi tertunda untuk dimulai? Jika demikian, apa lagi yang terjadi di thread utama pada saat itu?"
  • "Apakah interaksi tersebut menyebabkan banyak pekerjaan rendering yang menunda proses menggambar pada bingkai berikutnya?"

Tabel berikut menampilkan beberapa data atribusi dasar yang dapat diperoleh dari library yang dapat membantu Anda mengetahui beberapa penyebab tingkat tinggi dari interaksi yang lambat di situs:

Kunci objek attribution Data
interactionTarget Pemilih CSS yang mengarah ke elemen yang menghasilkan nilai INP halaman—misalnya, button#save.
interactionType Jenis interaksi, baik dari klik, ketukan, atau input keyboard.
inputDelay* Penundaan input interaksi.
processingDuration* Waktu sejak pemroses peristiwa pertama mulai berjalan sebagai respons terhadap interaksi pengguna hingga semua pemrosesan pemroses peristiwa selesai.
presentationDelay* Penundaan presentasi interaksi, yang terjadi mulai dari saat pengendali peristiwa selesai hingga saat frame berikutnya di-paint.
longAnimationFrameEntries* Entri dari LoAF yang terkait dengan interaksi. Lihat bagian berikutnya untuk mengetahui info tambahan.
*Baru di versi 4

Mulai dari library web-vitals versi 4, Anda bisa mendapatkan insight yang lebih mendalam tentang interaksi bermasalah melalui data yang disediakannya dengan perincian fase INP (penundaan input, durasi pemrosesan, dan penundaan presentasi) serta Long Animation Frame API (LoAF).

Long Animation Frame API (LoAF)

Dukungan Browser

  • 123
  • 123
  • x
  • x

Sumber

Men-debug interaksi menggunakan data kolom adalah tugas yang menantang. Namun, dengan data dari LoAF, sekarang Anda dapat memperoleh insight yang lebih baik tentang penyebab di balik interaksi yang lambat, karena LoAF menampilkan sejumlah waktu mendetail dan data lain yang dapat Anda gunakan untuk menemukan penyebab tepatnya—dan yang lebih penting, dari mana sumber masalah berada di kode situs Anda.

Build atribusi dari library web-vitals mengekspos array entri LoAF di bagian kunci longAnimationFrameEntries dari objek attribution. Tabel berikut mencantumkan beberapa kunci data yang dapat Anda temukan di setiap entri LoAF:

Kunci objek entri LoAF Data
duration Durasi bingkai animasi yang panjang, hingga saat tata letak selesai, tetapi tidak termasuk proses menggambar dan mengomposisikan.
blockingDuration Jumlah total waktu dalam frame yang tidak dapat merespons browser dengan cepat karena tugas yang panjang. Waktu pemblokiran ini dapat meliputi tugas panjang yang menjalankan JavaScript, serta tugas rendering panjang berikutnya dalam bingkai.
firstUIEventTimestamp Stempel waktu saat peristiwa diantrekan selama frame. Berguna untuk mencari tahu awal penundaan input interaksi.
startTime Stempel waktu awal frame.
renderStart Saat pekerjaan rendering untuk bingkai dimulai. Hal ini termasuk callback requestAnimationFrame (dan callback ResizeObserver jika berlaku), tetapi kemungkinan sebelum pekerjaan gaya/tata letak dimulai.
styleAndLayoutStart Saat pekerjaan gaya/tata letak dalam bingkai terjadi. Dapat berguna untuk mengetahui panjang gaya/tata letak saat mencari tahu stempel waktu lain yang tersedia.
scripts Array item yang berisi informasi atribusi skrip yang berkontribusi pada INP halaman.
Visualisasi frame animasi panjang menurut model LoAF.
Diagram pengaturan waktu frame animasi panjang menurut LoAF API (minus blockingDuration).

Semua informasi ini dapat memberi tahu Anda banyak hal tentang apa yang membuat interaksi menjadi lambat—tetapi array scripts yang muncul oleh entri LoAF harus menjadi perhatian khusus:

Kunci objek atribusi skrip Data
invoker Invoker. Hal ini dapat berbeda-beda berdasarkan jenis invoker yang dijelaskan di baris berikutnya. Contoh invoker dapat berupa nilai seperti 'IMG#id.onload', 'Window.requestAnimationFrame', atau 'Response.json.then'.
invokerType Jenis invoker. Dapat berupa 'user-callback', 'event-listener', 'resolve-promise', 'reject-promise', 'classic-script', atau 'module-script'.
sourceURL URL ke skrip tempat asal bingkai animasi panjang.
sourceCharPosition Posisi karakter dalam skrip yang diidentifikasi oleh sourceURL.
sourceFunctionName Nama fungsi dalam skrip yang diidentifikasi.

Setiap entri dalam array ini berisi data yang ditampilkan dalam tabel ini, yang memberi Anda informasi tentang skrip yang menyebabkan interaksi lambat—dan penyebabnya.

Ukur dan identifikasi penyebab umum di balik interaksi yang lambat

Untuk memberi Anda gambaran tentang cara menggunakan informasi ini, panduan ini sekarang akan menjelaskan cara menggunakan data LoAF yang ditampilkan di library web-vitals untuk menentukan beberapa penyebab di balik interaksi yang lambat.

Durasi pemrosesan yang panjang

Durasi pemrosesan interaksi adalah waktu yang diperlukan callback pengendali peristiwa terdaftar interaksi untuk dijalankan hingga selesai dan hal lain yang mungkin terjadi di antaranya. Durasi pemrosesan yang tinggi ditampilkan oleh library web-vitals:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {processingDuration} = attribution; // 512.5
});

Wajar untuk berpikir bahwa penyebab utama di balik interaksi yang lambat adalah karena kode pengendali peristiwa Anda membutuhkan waktu terlalu lama untuk dijalankan, tetapi itu tidak selalu terjadi. Setelah mengonfirmasi bahwa inilah masalahnya, Anda dapat menggali lebih dalam dengan data LoAF:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {processingDuration} = attribution; // 512.5

  // Get the longest script from LoAF covering `processingDuration`:
  const loaf = attribution.longAnimationFrameEntries.at(-1);
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  if (script) {
    // Get attribution for the long-running event handler:
    const {invokerType} = script;        // 'event-listener'
    const {invoker} = script;            // 'BUTTON#update.onclick'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

Seperti yang dapat Anda lihat dalam cuplikan kode sebelumnya, Anda dapat menggunakan data LoAF untuk melacak penyebab sebenarnya di balik interaksi dengan nilai durasi pemrosesan yang tinggi, termasuk:

  • Elemen dan pemroses peristiwa terdaftarnya.
  • File skrip—dan posisi karakter di dalamnya—berisi kode pengendali peristiwa yang berjalan lama.
  • Nama fungsi.

Jenis data ini sangat berharga. Anda tidak perlu lagi bersusah payah mencari tahu dengan tepat interaksi mana—atau pengendali peristiwa mana—yang bertanggung jawab atas nilai durasi pemrosesan yang tinggi. Selain itu, karena skrip pihak ketiga sering kali dapat mendaftarkan pengendali peristiwanya sendiri, Anda dapat menentukan apakah kode Anda yang bertanggung jawab atau bukan. Untuk kode yang dapat Anda kontrol, sebaiknya lihat cara mengoptimalkan tugas yang panjang.

Penundaan input yang lama

Meskipun pengendali peristiwa yang berjalan lama adalah hal biasa, ada bagian lain dari interaksi yang perlu dipertimbangkan. Satu bagian terjadi sebelum durasi pemrosesan, yang dikenal sebagai penundaan input. Ini adalah waktu dari saat pengguna memulai interaksi, hingga saat callback pengendali peristiwanya mulai berjalan dan terjadi saat thread utama sudah memproses tugas lain. Build atribusi library web-vitals dapat memberi tahu Anda lamanya penundaan input untuk interaksi:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {inputDelay} = attribution; // 125.59439536
});

Jika Anda melihat bahwa beberapa interaksi memiliki penundaan input yang tinggi, maka Anda harus mencari tahu apa yang terjadi di halaman pada saat interaksi yang menyebabkan penundaan input yang lama—dan itu sering kali bermuara pada apakah interaksi terjadi saat halaman dimuat, atau setelahnya.

Apakah pada saat pemuatan halaman?

Thread utama sering kali tersibuk saat halaman dimuat. Selama waktu ini, segala macam tugas sedang diantrekan dan diproses, dan jika pengguna mencoba berinteraksi dengan halaman saat semua pekerjaan ini terjadi, hal itu dapat menunda interaksi. Halaman yang memuat banyak JavaScript dapat memulai pekerjaan untuk mengompilasi dan mengevaluasi skrip, serta menjalankan fungsi yang membuat halaman siap untuk interaksi pengguna. Pekerjaan ini bisa menghalangi jika pengguna kebetulan berinteraksi saat aktivitas ini terjadi, dan Anda dapat mencari tahu apakah itu yang terjadi untuk pengguna situs Anda:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {inputDelay} = attribution; // 125.59439536

  // Get the longest script from the first LoAF entry:
  const loaf = attribution.longAnimationFrameEntries[0];
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  if (script) {
    // Invoker types can describe if script eval blocked the main thread:
    const {invokerType} = script;    // 'classic-script' | 'module-script'
    const {sourceLocation} = script; // 'https://example.com/app.js'
  }
});

Jika Anda mencatat data ini di kolom dan melihat penundaan input dan jenis invoker 'classic-script' atau 'module-script' yang tinggi, dapat dikatakan bahwa skrip di situs Anda memerlukan waktu lama untuk dievaluasi, dan memblokir thread utama cukup lama untuk menunda interaksi. Anda dapat mengurangi waktu pemblokiran ini dengan membagi skrip menjadi paket-paket yang lebih kecil, menunda kode yang awalnya tidak digunakan untuk dimuat di lain waktu, dan mengaudit situs untuk menemukan kode yang tidak terpakai yang bisa Anda hapus sekaligus.

Apakah setelah pemuatan halaman?

Meskipun penundaan input sering terjadi saat halaman dimuat, hal tersebut mungkin saja terjadi setelah halaman dimuat, karena penyebab yang sama sekali berbeda. Penyebab umum penundaan input setelah pemuatan halaman adalah kode yang berjalan secara berkala karena panggilan setInterval sebelumnya, atau bahkan callback peristiwa yang diantrekan untuk dijalankan sebelumnya, dan masih diproses.

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {inputDelay} = attribution; // 125.59439536

  // Get the longest script from the first LoAF entry:
  const loaf = attribution.longAnimationFrameEntries[0];
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  if (script) {
    const {invokerType} = script;        // 'user-callback'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

Seperti halnya memecahkan masalah nilai durasi pemrosesan yang tinggi, penundaan input yang tinggi karena penyebab yang disebutkan di atas akan memberi Anda data atribusi skrip yang mendetail. Namun, yang berbeda adalah jenis invoker akan berubah berdasarkan sifat pekerjaan yang menunda interaksi:

  • 'user-callback' menunjukkan tugas pemblokiran berasal dari setInterval, setTimeout, atau bahkan requestAnimationFrame.
  • 'event-listener' menunjukkan bahwa tugas pemblokiran berasal dari input sebelumnya yang diantrekan dan masih diproses.
  • 'resolve-promise' dan 'reject-promise' berarti tugas pemblokiran berasal dari beberapa pekerjaan asinkron yang dimulai sebelumnya, dan diselesaikan atau ditolak pada saat pengguna mencoba berinteraksi dengan halaman, sehingga menunda interaksi.

Dalam kasus apa pun, data atribusi skrip akan memberi gambaran tentang tempat untuk mulai mencari, dan apakah penundaan input disebabkan oleh kode Anda sendiri, atau skrip pihak ketiga.

Penundaan presentasi yang lama

Penundaan presentasi adalah mil terakhir dari interaksi, dan dimulai saat pengendali peristiwa interaksi selesai, hingga titik saat frame berikutnya digambar. Hal ini terjadi saat pekerjaan di pengendali peristiwa karena interaksi mengubah status visual antarmuka pengguna. Seperti halnya durasi pemrosesan dan penundaan input, library web-vitals dapat memberi tahu Anda berapa lama penundaan presentasi itu untuk sebuah interaksi:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {presentationDelay} = attribution; // 113.32307691
});

Jika Anda mencatat data ini dan melihat penundaan presentasi yang tinggi untuk interaksi yang berkontribusi pada INP situs Anda, penyebabnya dapat bervariasi, tetapi berikut adalah beberapa penyebab yang harus diperhatikan.

Pekerjaan gaya dan tata letak yang mahal

Penundaan presentasi yang lama dapat menjadi perhitungan ulang gaya dan pekerjaan tata letak yang mahal yang muncul dari sejumlah penyebab, termasuk pemilih CSS yang kompleks dan ukuran DOM yang besar. Anda dapat mengukur durasi pekerjaan ini dengan pengaturan waktu LoAF yang ditampilkan di library web-vitals:

import {onINP} from 'web-vitals/attribution';

onINP(({name, value, attribution}) => {
  const {presentationDelay} = attribution; // 113.32307691

  // Get the longest script from the last LoAF entry:
  const loaf = attribution.longAnimationFrameEntries.at(-1);
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  // Get necessary timings:
  const {startTime} = loaf; // 2120.5
  const {duration} = loaf;  // 1002

  // Figure out the ending timestamp of the frame (approximate):
  const endTime = startTime + duration; // 3122.5

  // Get the start timestamp of the frame's style/layout work:
  const {styleAndLayoutStart} = loaf; // 3011.17692309

  // Calculate the total style/layout duration:
  const styleLayoutDuration = endTime - styleAndLayoutStart; // 111.32307691

  if (script) {
    // Get attribution for the event handler that triggered
    // the long-running style and layout operation:
    const {invokerType} = script;        // 'event-listener'
    const {invoker} = script;            // 'BUTTON#update.onclick'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

LoAF tidak akan memberi tahu Anda durasi gaya dan pekerjaan tata letak untuk bingkai, tetapi akan memberi tahu Anda kapan gaya tersebut dimulai. Dengan stempel waktu awal ini, Anda dapat menggunakan data lain dari LoAF untuk menghitung durasi yang akurat dari pekerjaan tersebut dengan menentukan waktu berakhir frame, dan mengurangi stempel waktu mulai pekerjaan gaya dan tata letak.

Callback requestAnimationFrame yang berjalan lama

Salah satu potensi penyebab penundaan presentasi yang lama adalah pekerjaan berlebihan yang dilakukan dalam callback requestAnimationFrame. Konten callback ini dijalankan setelah pengendali peristiwa selesai berjalan, tetapi tepat sebelum penghitungan ulang gaya dan pekerjaan tata letak.

Callback ini dapat membutuhkan waktu yang cukup lama untuk diselesaikan jika pekerjaan yang dilakukan di dalamnya rumit. Jika Anda mencurigai nilai penundaan presentasi yang tinggi adalah karena pekerjaan yang Anda lakukan dengan requestAnimationFrame, Anda dapat menggunakan data LoAF yang ditampilkan oleh library web-vitals untuk mengidentifikasi skenario berikut:

onINP(({name, value, attribution}) => {
  const {presentationDelay} = attribution; // 543.1999999880791

  // Get the longest script from the last LoAF entry:
  const loaf = attribution.longAnimationFrameEntries.at(-1);
  const script = loaf?.scripts.sort((a, b) => b.duration - a.duration)[0];

  // Get the render start time and when style and layout began:
  const {renderStart} = loaf;         // 2489
  const {styleAndLayoutStart} = loaf; // 2989.5999999940395

  // Calculate the `requestAnimationFrame` callback's duration:
  const rafDuration = styleAndLayoutStart - renderStart; // 500.59999999403954

  if (script) {
    // Get attribution for the event handler that triggered
    // the long-running requestAnimationFrame callback:
    const {invokerType} = script;        // 'user-callback'
    const {invoker} = script;            // 'FrameRequestCallback'
    const {sourceURL} = script;          // 'https://example.com/app.js'
    const {sourceCharPosition} = script; // 83
    const {sourceFunctionName} = script; // 'update'
  }
});

Jika Anda melihat bahwa sebagian besar waktu penundaan presentasi dihabiskan dalam callback requestAnimationFrame, pastikan pekerjaan yang Anda lakukan di callback ini terbatas pada melakukan tugas yang menghasilkan update aktual pada antarmuka pengguna. Pekerjaan lain yang tidak menyentuh DOM atau memperbarui gaya akan menunda perekaman frame berikutnya secara tidak sengaja, jadi berhati-hatilah!

Kesimpulan

Data lapangan adalah sumber informasi terbaik yang dapat Anda gunakan untuk memahami interaksi mana yang bermasalah bagi pengguna aktual di lapangan. Dengan mengandalkan alat pengumpulan data kolom seperti library JavaScript web-vitals (atau penyedia RUM), Anda dapat lebih yakin tentang interaksi mana yang paling bermasalah, lalu beralih ke reproduksi interaksi yang bermasalah di lab dan kemudian memperbaikinya.

Banner besar dari Unsplash, oleh Federico Respini.