Menemukan interaksi lambat di lapangan

Pelajari cara menemukan interaksi lambat dalam data lapangan situs Anda sehingga Anda dapat menemukan peluang untuk meningkatkan Interaction to Next Paint-nya.

Data lapangan adalah data yang memberi tahu Anda cara pengguna sebenarnya berinteraksi dengan situs Anda. Hal ini akan memunculkan masalah yang tidak dapat Anda temukan hanya dalam data lab. Terkait Interaction to Next Paint (INP), 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 Anda dengan cepat menggunakan data kolom dari Laporan Pengalaman Pengguna Chrome (CrUX) untuk melihat apakah situs Anda memiliki masalah dengan INP. Selanjutnya, Anda akan mempelajari cara menggunakan build atribusi library JavaScript web-vitals—dan insight baru yang diberikannya dari Long Animation Frames API (LoAF)—untuk mengumpulkan dan menafsirkan data lapangan untuk interaksi lambat di situs Anda.

Mulai dengan CrUX untuk mengevaluasi INP situs Anda

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

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

  • Halaman individual dan seluruh origin menggunakan PageSpeed Insights.
  • Jenis halaman. Misalnya, banyak situs e-commerce memiliki jenis Halaman Detail Produk dan Halaman Daftar Produk. Anda bisa mendapatkan data CrUX untuk jenis halaman unik di Search Console.

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

Data kolom seperti yang ditampilkan oleh CrUX di PageSpeed Insights, yang menampilkan LCP, INP, CLS di tiga Data Web Inti, dan TTFB, FCP sebagai metrik diagnostik, serta FID sebagai metrik Data Web Inti yang tidak digunakan lagi.
Pembacaan data CrUX seperti yang terlihat di PageSpeed Insights. Dalam contoh ini, INP halaman web yang diberikan perlu ditingkatkan.

Data ini berguna karena memberi tahu Anda apakah ada masalah. Namun, CrUX tidak dapat memberi tahu Anda apa yang menyebabkan masalah. Ada banyak solusi Pemantauan Pengguna Nyata (RUM) yang tersedia yang akan membantu Anda mengumpulkan data lapangan sendiri dari pengguna situs Anda untuk membantu Anda menjawabnya, dan salah satu opsinya adalah mengumpulkan data lapangan tersebut 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. Anda dapat menggunakannya untuk merekam sejumlah metrik, termasuk INP di browser yang mendukungnya.

Browser Support

  • Chrome: 96.
  • Edge: 96.
  • Firefox Technology Preview: supported.
  • Safari: not supported.

Source

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 harus 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 daripada yang akan dilakukan CrUX. Di sinilah build atribusi library web-vitals berperan.

Melangkah lebih jauh dengan build atribusi library web-vitals

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

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

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

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

  • "Apakah pengguna berinteraksi dengan halaman saat halaman dimuat?"
  • "Apakah pengendali peristiwa interaksi berjalan dalam waktu yang lama?"
  • "Apakah kode pengendali peristiwa interaksi tertunda saat dimulai? Jika ya, apa lagi yang terjadi di thread utama pada saat itu?"
  • "Apakah interaksi menyebabkan banyak pekerjaan rendering yang menunda frame berikutnya dirender?"

Tabel berikut menunjukkan beberapa data atribusi dasar yang bisa Anda dapatkan dari library yang dapat membantu Anda mengetahui beberapa penyebab utama interaksi lambat di situs Anda:

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 waktu frame berikutnya ditampilkan.
longAnimationFrameEntries* Entri dari LoAF yang terkait dengan interaksi. Lihat bagian berikutnya untuk mengetahui info tambahan.
*Baru di versi 4

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

Long Animation Frames API (LoAF)

Browser Support

  • Chrome: 123.
  • Edge: 123.
  • Firefox: not supported.
  • Safari: not supported.

Source

Men-debug interaksi menggunakan data kolom adalah tugas yang menantang. Namun, dengan data dari LoAF, kini Anda bisa mendapatkan insight yang lebih baik tentang penyebab interaksi yang lambat, karena LoAF mengekspos sejumlah waktu yang mendetail dan data lain yang dapat Anda gunakan untuk menentukan penyebab yang tepat—dan yang lebih penting, di mana sumber masalahnya dalam kode situs Anda.

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

Kunci objek entri LoAF Data
duration Durasi frame animasi panjang, hingga tata letak selesai, tetapi tidak termasuk menggambar dan menggabungkan.
blockingDuration Total durasi waktu dalam frame saat browser tidak dapat merespons dengan cepat karena tugas yang panjang. Waktu pemblokiran ini dapat mencakup tugas panjang yang menjalankan JavaScript, serta tugas rendering panjang berikutnya dalam frame.
firstUIEventTimestamp Stempel waktu saat peristiwa diantrekan selama frame. Berguna untuk mengetahui awal penundaan input interaksi.
startTime Stempel waktu awal frame.
renderStart Saat pekerjaan rendering untuk frame dimulai. Hal ini mencakup semua callback requestAnimationFrame (dan callback ResizeObserver jika berlaku), tetapi berpotensi sebelum pekerjaan gaya/tata letak dimulai.
styleAndLayoutStart Saat pekerjaan gaya/tata letak dalam frame terjadi. Dapat berguna dalam menentukan durasi pekerjaan gaya/tata letak saat memperhitungkan 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 (dikurangi blockingDuration).

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

Kunci objek atribusi skrip Data
invoker Pemanggil. Hal ini dapat bervariasi berdasarkan jenis pemanggil yang dijelaskan di baris berikutnya. Contoh pemanggil dapat berupa nilai seperti 'IMG#id.onload', 'Window.requestAnimationFrame', atau 'Response.json.then'.
invokerType Jenis pemanggil. Dapat berupa 'user-callback', 'event-listener', 'resolve-promise', 'reject-promise', 'classic-script', atau 'module-script'.
sourceURL URL ke skrip tempat frame animasi panjang berasal.
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 bagaimana skrip tersebut bertanggung jawab.

Mengukur dan mengidentifikasi penyebab umum di balik interaksi yang lambat

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

Durasi pemrosesan yang lama

Durasi pemrosesan interaksi adalah waktu yang diperlukan untuk menjalankan callback pengendali peristiwa terdaftar interaksi 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 jika Anda berpikir bahwa penyebab utama interaksi yang lambat adalah kode pengendali peristiwa Anda berjalan terlalu lama, tetapi tidak selalu demikian. Setelah mengonfirmasi bahwa hal ini adalah masalahnya, Anda dapat mempelajari 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.toSorted((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 pasti di balik interaksi dengan nilai durasi pemrosesan yang tinggi, termasuk:

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

Jenis data ini sangat berharga. Anda tidak perlu lagi melakukan pekerjaan berat untuk mengetahui secara pasti interaksi mana—atau pengendali peristiwanya—yang menyebabkan nilai durasi pemrosesan tinggi. Selain itu, karena skrip pihak ketiga sering kali mendaftarkan pengendali peristiwanya sendiri, Anda dapat menentukan apakah kode Anda yang bertanggung jawab atau tidak. Untuk kode yang dapat Anda kontrol, sebaiknya Anda mempelajari cara mengoptimalkan tugas panjang.

Penundaan input yang lama

Meskipun pemroses peristiwa yang berjalan lama sering terjadi, ada bagian lain dari interaksi yang perlu dipertimbangkan. Satu bagian terjadi sebelum durasi pemrosesan, yang dikenal sebagai penundaan input. Ini adalah waktu sejak pengguna memulai interaksi, hingga saat callback handler peristiwanya mulai berjalan dan terjadi saat thread utama sudah memproses tugas lain. Build atribusi library web-vitals dapat memberi tahu Anda durasi penundaan input untuk suatu 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, Anda harus mencari tahu apa yang terjadi di halaman pada saat interaksi yang menyebabkan penundaan input yang lama—dan hal ini sering kali bergantung pada apakah interaksi terjadi saat halaman sedang dimuat, atau setelahnya.

Apakah terjadi saat pemuatan halaman?

Thread utama sering kali paling sibuk saat halaman dimuat. Selama waktu ini, semua jenis tugas diantrekan dan diproses, dan jika pengguna mencoba berinteraksi dengan halaman saat semua pekerjaan ini terjadi, interaksi dapat tertunda. Halaman yang memuat banyak JavaScript dapat memulai pekerjaan untuk mengompilasi dan mengevaluasi skrip, serta menjalankan fungsi yang menyiapkan halaman untuk interaksi pengguna. Pekerjaan ini dapat mengganggu jika pengguna berinteraksi saat aktivitas ini terjadi, dan Anda dapat mengetahui apakah hal itu terjadi pada 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.toSorted((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 merekam data ini di kolom dan melihat penundaan input yang tinggi serta jenis pemanggil 'classic-script' atau 'module-script', maka dapat dikatakan bahwa skrip di situs Anda membutuhkan waktu yang lama untuk dievaluasi, dan memblokir thread utama cukup lama untuk menunda interaksi. Anda dapat mengurangi waktu pemblokiran ini dengan memecah skrip menjadi paket yang lebih kecil, menunda kode yang awalnya tidak digunakan untuk dimuat di lain waktu, dan mengaudit situs Anda untuk menemukan kode yang tidak digunakan yang dapat Anda hapus sepenuhnya.

Apakah setelah pemuatan halaman?

Meskipun penundaan input sering terjadi saat halaman dimuat, penundaan input juga dapat terjadi setelah halaman dimuat, karena penyebab yang sama sekali berbeda. Penyebab umum penundaan input setelah pemuatan halaman dapat berupa 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.toSorted((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 pemecahan masalah nilai durasi pemrosesan yang tinggi, penundaan input yang tinggi karena penyebab yang disebutkan sebelumnya akan memberi Anda data atribusi skrip yang mendetail. Namun, yang berbeda adalah jenis pemanggil akan berubah berdasarkan sifat pekerjaan yang menunda interaksi:

  • 'user-callback' menunjukkan bahwa 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 tugas asinkron yang dimulai sebelumnya, dan diselesaikan atau ditolak pada saat pengguna mencoba berinteraksi dengan halaman, sehingga menunda interaksi.

Bagaimanapun juga, data atribusi skrip akan memberi Anda 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 bagian terakhir dari interaksi, dan dimulai saat pengendali peristiwa interaksi selesai, hingga titik saat frame berikutnya digambar. Pergeseran ini terjadi saat pekerjaan di pengendali peristiwa karena interaksi mengubah status visual antarmuka pengguna. Seperti durasi pemrosesan dan penundaan input, library web-vitals dapat memberi tahu Anda berapa lama penundaan presentasi untuk suatu interaksi:

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

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

Jika Anda merekam data ini dan melihat penundaan presentasi yang tinggi untuk interaksi yang berkontribusi pada INP situs Anda, penyebabnya dapat bervariasi, tetapi berikut beberapa penyebab yang perlu diwaspadai.

Pekerjaan gaya dan tata letak yang mahal

Penundaan presentasi yang lama dapat menyebabkan penghitungan ulang gaya dan tata letak yang mahal akibat sejumlah penyebab, termasuk pemilih CSS yang kompleks dan ukuran DOM yang besar. Anda dapat mengukur durasi pekerjaan ini dengan waktu LoAF yang muncul 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.toSorted((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 pekerjaan gaya dan tata letak untuk frame, tetapi akan memberi tahu Anda kapan pekerjaan tersebut dimulai. Dengan stempel waktu awal ini, Anda dapat menggunakan data lain dari LoAF untuk menghitung durasi akurat pekerjaan tersebut dengan menentukan waktu berakhir frame, dan menguranginya dengan stempel waktu awal 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. Isi callback ini dieksekusi setelah pemroses peristiwa selesai berjalan, tetapi tepat sebelum penghitungan ulang gaya dan pekerjaan tata letak.

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

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.toSorted((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 dalam callback ini terbatas pada melakukan pekerjaan yang menghasilkan pembaruan sebenarnya pada antarmuka pengguna. Pekerjaan lain yang tidak menyentuh DOM atau memperbarui gaya akan menunda rendering frame berikutnya secara tidak perlu, jadi berhati-hatilah.

Kesimpulan

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

Gambar utama dari Unsplash, oleh Federico Respini.