Menemukan interaksi lambat di lapangan

Pelajari cara menemukan interaksi yang lambat di data kolom situs agar Anda dapat menemukan peluang untuk meningkatkan Interaction to Next Paint.

Data kolom adalah data yang menunjukkan pengalaman pengguna yang sebenarnya di situs Anda. Alat ini menyoroti masalah yang tidak dapat Anda temukan di data lab saja. Jika Interaction to Next Paint (INP) diperhatikan, 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 lapangan dari Laporan Pengalaman Pengguna (CrUX) Chrome untuk mengetahui apakah situs Anda mengalami 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 kolom untuk interaksi yang lambat di situs Anda.

Mulai dengan CrUX untuk mengevaluasi INP situs Anda

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

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

  • Masing-masing halaman dan seluruh origin menggunakan PageSpeed Insights.
  • Jenis halaman. Misalnya, banyak situs e-commerce memiliki jenis Halaman Detail Produk dan Halaman Listingan Produk. Anda bisa mendapatkan 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 untuk URL tersebut—jika tersedia—akan ditampilkan untuk beberapa metrik, termasuk INP. Anda juga dapat menggunakan tombol alih guna 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 Core Web Vitals, dan TTFB, FCP sebagai metrik diagnostik, dan FID sebagai metrik Core Web Vital 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 akan memberi tahu Anda jika Anda mengalami masalah. Namun, yang tidak dapat dilakukan CrUX adalah memberi tahu Anda apa yang menyebabkan masalah. Ada banyak solusi Pemantauan Pengguna Nyata (RUM) yang tersedia untuk membantu Anda mengumpulkan data kolom dari pengguna situs untuk menjawabnya. Salah satu opsinya adalah mengumpulkan sendiri data kolom tersebut menggunakan library JavaScript web-vitals.

Kumpulkan 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 mencatat sejumlah metrik, termasuk INP di browser yang mendukungnya.

Dukungan Browser

  • Chrome: 96.
  • Edge: 96.
  • Firefox: tidak didukung.
  • Safari: tidak didukung.

Sumber

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

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 mengirim 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 dari apa yang dapat 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 dapat Anda peroleh 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 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
});
Cara log konsol dari library web-vitals muncul. Konsol dalam contoh ini menunjukkan nama metrik (INP), nilai INP (56), tempat nilai tersebut berada dalam nilai minimum INP (baik), dan berbagai bit 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, build atribusi memberikan banyak data yang dapat digunakan untuk membantu memahami alasan interaksi yang lambat, termasuk bagian interaksi mana yang harus Anda fokuskan. Materi ini dapat membantu Anda menjawab pertanyaan penting seperti:

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

Tabel berikut menunjukkan beberapa data atribusi dasar yang bisa Anda dapatkan dari library yang dapat membantu mengetahui beberapa penyebab umum lambatnya interaksi 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 dari saat pemroses peristiwa pertama mulai berjalan sebagai respons terhadap interaksi pengguna hingga semua pemrosesan pemroses peristiwa selesai.
presentationDelay* Penundaan presentasi interaksi, yang terjadi sejak pengendali peristiwa selesai hingga frame berikutnya digambar.
longAnimationFrameEntries* Entri dari LoAF yang terkait dengan interaksi. Lihat halaman 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 diberikannya dengan pengelompokan fase INP (penundaan input, durasi pemrosesan, dan penundaan presentasi) serta Long Animation Frames API (LoAF).

Long Animation Frames API (LoAF)

Dukungan Browser

  • Chrome: 123.
  • Edge: 123.
  • Firefox: tidak didukung.
  • Safari: tidak didukung.

Sumber

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

Build atribusi library web-vitals menampilkan array entri LoAF di bagian kunci longAnimationFrameEntries dari objek attribution. Tabel berikut mencantumkan beberapa bit data penting 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 pengomposisian.
blockingDuration Jumlah total waktu dalam frame yang tidak dapat direspons dengan cepat oleh browser karena tugas yang lama. Waktu pemblokiran ini bisa mencakup tugas lama yang menjalankan JavaScript, serta tugas rendering panjang berikutnya di frame.
firstUIEventTimestamp Stempel waktu saat peristiwa dimasukkan dalam antrean selama frame. Berguna untuk mencari tahu awal penundaan input interaksi.
startTime Stempel waktu awal frame.
renderStart Saat pekerjaan rendering untuk frame dimulai. Hal ini mencakup callback requestAnimationFrame (dan callback ResizeObserver jika ada), tetapi kemungkinan sebelum pekerjaan gaya/tata letak dimulai.
styleAndLayoutStart Saat pekerjaan gaya/tata letak dalam bingkai terjadi. Dapat berguna untuk mengetahui panjang pekerjaan gaya/tata letak saat mencari stempel waktu lain yang tersedia.
scripts Array item yang berisi informasi atribusi skrip yang berkontribusi pada INP halaman.
Visualisasi bingkai animasi panjang menurut model LoAF.
Diagram pengaturan waktu frame animasi yang panjang menurut LoAF API (dikurangi blockingDuration).

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

Kunci objek atribusi skrip Data
invoker Pemanggil. Hal ini dapat bervariasi 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 frame 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 bertanggung jawab atas interaksi yang lambat—dan bagaimana hal itu bertanggung jawab.

Mengukur dan mengidentifikasi penyebab umum di balik interaksi yang lambat

Untuk memberikan gambaran 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 lambatnya interaksi.

Durasi pemrosesan yang lama

Durasi pemrosesan interaksi adalah waktu yang dibutuhkan callback pengendali peristiwa terdaftar milik interaksi untuk berjalan sampai selesai dan hal lain yang mungkin terjadi di antara callback tersebut. Durasi pemrosesan yang tinggi ditampilkan oleh library web-vitals:

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

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

Masuk akal untuk berpikir bahwa penyebab utama di balik interaksi yang lambat adalah bahwa kode pengendali peristiwa Anda membutuhkan waktu terlalu lama untuk dijalankan, tetapi itu tidak selalu terjadi. Setelah mengonfirmasi bahwa ini adalah 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 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 bersusah payah untuk 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 mengoptimalkan tugas yang berjalan lama.

Penundaan input panjang

Meskipun pengendali peristiwa yang berjalan lama adalah hal yang umum, ada bagian lain dari interaksi yang perlu dipertimbangkan. Salah 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 ketika thread utama sudah memproses tugas lain. Build atribusi library web-vitals dapat memberi tahu Anda durasi 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 hal itu terjadi selama pemuatan halaman?

Thread utama sering kali tersibuk saat halaman dimuat. Selama waktu ini, semua jenis tugas 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 mengeksekusi fungsi yang menyiapkan halaman untuk interaksi pengguna. Pekerjaan ini dapat menjadi hambatan jika pengguna berinteraksi saat aktivitas ini terjadi, dan Anda dapat mengetahui apakah hal itu terjadi bagi pengguna {i>website<i} 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 ini dan melihat penundaan input dan jenis invoker yang tinggi seperti 'classic-script' atau 'module-script', dapat disimpulkan bahwa evaluasi skrip di situs Anda memerlukan waktu yang lama, dan memblokir thread utama cukup lama sehingga menunda interaksi. Anda dapat mengurangi waktu pemblokiran ini dengan membagi skrip menjadi beberapa paket yang lebih kecil, menunda kode yang awalnya tidak digunakan untuk dimuat di lain waktu, dan mengaudit situs Anda untuk kode tidak terpakai yang dapat Anda hapus sepenuhnya.

Apakah itu muncul setelah halaman dimuat?

Meskipun penundaan input sering terjadi saat halaman dimuat, penundaan input mungkin 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 berjalan 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'
  }
});

Sama halnya dengan pemecahan masalah terkait 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 invoker 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 telah diantrekan dan masih diproses.
  • 'resolve-promise' dan 'reject-promise' berarti bahwa tugas pemblokiran berasal dari beberapa pekerjaan asinkron yang dimulai lebih awal, dan diselesaikan atau ditolak pada saat pengguna mencoba berinteraksi dengan halaman, sehingga menunda interaksi.

Dalam situasi apa pun, data atribusi skrip akan memberi Anda gambaran tentang dari mana harus mulai mencari, dan apakah penundaan input disebabkan oleh kode Anda sendiri, atau akibat skrip pihak ketiga.

Presentasi lama tertunda

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

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

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

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

Biaya pengerjaan gaya dan tata letak yang mahal

Penundaan presentasi yang lama dapat menimbulkan perhitungan ulang gaya dan pekerjaan tata letak yang mahal yang muncul karena 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 pekerjaan gaya dan tata letak untuk bingkai, tetapi akan memberi tahu Anda kapan itu dimulai. Dengan stempel waktu awal ini, Anda dapat menggunakan data lain dari LoAF untuk menghitung durasi akurat dari pekerjaan tersebut dengan menentukan waktu berakhir frame, dan mengurangi stempel waktu mulai gaya dan tata letak bekerja dari waktu tersebut.

Callback requestAnimationFrame yang berjalan lama

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

Callback ini bisa memakan waktu yang cukup lama untuk diselesaikan jika pekerjaan yang dilakukan di dalamnya bersifat kompleks. 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 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 di callback requestAnimationFrame, pastikan pekerjaan yang Anda lakukan dalam callback ini dibatasi untuk menjalankan pekerjaan yang menghasilkan update aktual pada antarmuka pengguna. Pekerjaan lain apa pun yang tidak menyentuh DOM atau memperbarui gaya akan menunda pelukisan bingkai berikutnya, jadi berhati-hatilah!

Kesimpulan

Data kolom adalah sumber informasi terbaik yang dapat Anda gunakan untuk memahami interaksi mana yang bermasalah bagi pengguna sebenarnya 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, kemudian beralih ke mereproduksi interaksi bermasalah di lab dan kemudian memperbaikinya.

Banner besar dari Unsplash, oleh Federico Respini.