Pelajari cara menemukan interaksi lambat di data lapangan situs Anda sehingga Anda dapat menemukan peluang untuk meningkatkan Interaction to Next Paint-nya.
Data lapangan adalah data yang memberi tahu Anda pengalaman pengguna yang sebenarnya di situs Anda. Metrik ini mengungkap masalah yang tidak dapat Anda temukan di data lab saja. 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 lapangan dari Chrome User Experience Report (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, 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 Core Web Vitals lainnya untuk:
- Setiap 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 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 ini berguna karena memberi tahu Anda jika 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 untuk membantu Anda menjawab pertanyaan tersebut, 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 lapangan dari pengguna situs Anda. Anda dapat menggunakannya untuk mencatat sejumlah metrik, termasuk INP di browser yang mendukungnya.
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 lapangan dari pengguna, Anda harus 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 saja tidak memberikan banyak informasi selain yang diberikan CrUX. Di sinilah build atribusi library web-vitals berperan.
Mempelajari lebih lanjut 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 ditampilkan 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
});
Selain INP halaman itu sendiri, build atribusi menyediakan banyak data yang dapat Anda gunakan untuk membantu memahami alasan interaksi yang lambat, termasuk bagian interaksi mana yang harus Anda fokuskan. Laporan 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 untuk dimulai? Jika ya, apa lagi yang terjadi di thread utama pada saat itu?"
- "Apakah interaksi tersebut 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 Anda mengetahui beberapa penyebab tingkat tinggi dari interaksi yang 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 saat frame berikutnya digambar. |
longAnimationFrameEntries*
|
Entri dari LoAF yang terkait dengan interaksi. Lihat bagian berikutnya untuk mengetahui info tambahan. |
Mulai versi 4 library web-vitals, Anda bisa mendapatkan insight yang lebih mendalam tentang interaksi yang bermasalah melalui data yang diberikan dengan perincian fase INP (penundaan input, durasi pemrosesan, dan penundaan presentasi) dan Long Animation Frames API (LoAF).
Long Animation Frames API (LoAF)
Men-debug interaksi menggunakan data lapangan adalah tugas yang menantang. Namun, dengan data dari LoAF, kini Anda dapat memperoleh 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 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 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 frame animasi panjang, hingga tata letak selesai, tetapi tidak termasuk rendering dan penggabungan. |
blockingDuration
|
Jumlah total waktu dalam frame saat browser tidak dapat merespons dengan cepat karena tugas berdurasi lama. Waktu pemblokiran ini dapat mencakup tugas berdurasi lama yang menjalankan JavaScript, serta tugas rendering berdurasi lama berikutnya dalam frame. |
firstUIEventTimestamp
|
Stempel waktu saat peristiwa dimasukkan ke dalam antrean selama frame. Berguna untuk mengetahui awal penundaan input interaksi. |
startTime
|
Stempel waktu awal frame. |
renderStart
|
Waktu saat pekerjaan rendering untuk frame dimulai. Hal ini mencakup 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. |
blockingDuration).
Semua informasi ini dapat memberi tahu Anda banyak hal tentang penyebab interaksi yang lambat, tetapi array scripts yang ditampilkan oleh 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 bertanggung jawab atas 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 membahas 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 callback pengendali peristiwa terdaftar interaksi untuk berjalan 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 mengira bahwa penyebab utama di balik interaksi yang lambat adalah kode pengendali peristiwa Anda memerlukan waktu terlalu lama untuk dijalankan, tetapi hal 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.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 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 peristiwa terdaftarnya.
- 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 upaya untuk mencari tahu 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 tidak. Untuk kode yang Anda kontrol, Anda perlu mempelajari pengoptimalan tugas berdurasi lama.
Penundaan input yang lama
Meskipun pengendali peristiwa yang berjalan lama adalah hal yang umum, 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 pengendali 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 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 itu sering kali bermuara pada apakah interaksi terjadi saat halaman dimuat, atau setelahnya.
Apakah terjadi selama pemuatan halaman?
Thread utama sering kali paling sibuk saat halaman dimuat. Selama waktu ini, berbagai tugas sedang diantrekan dan diproses, dan jika pengguna mencoba berinteraksi dengan halaman saat semua pekerjaan ini sedang berlangsung, 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 ini 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 mencatat data ini di lapangan dan melihat penundaan input yang tinggi dan jenis pemanggil 'classic-script' atau 'module-script', dapat dikatakan bahwa skrip di situs Anda memerlukan waktu lama untuk dievaluasi, dan memblokir thread utama cukup lama sehingga menunda interaksi. Anda dapat mengurangi waktu pemblokiran ini dengan memecah skrip menjadi paket yang lebih kecil, menunda pemuatan kode yang awalnya tidak digunakan ke lain waktu, dan mengaudit situs untuk menemukan kode yang tidak digunakan yang dapat dihapus sepenuhnya.
Apakah setelah pemuatan halaman?
Meskipun penundaan input sering terjadi saat halaman dimuat, penundaan tersebut 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 darisetInterval,setTimeout, atau bahkanrequestAnimationFrame.'event-listener'menunjukkan bahwa tugas pemblokiran berasal dari input sebelumnya yang diantrekan dan masih diproses.'resolve-promise'dan'reject-promise'berarti bahwa tugas pemblokiran berasal dari beberapa pekerjaan asinkron yang dimulai sebelumnya, dan diselesaikan atau ditolak pada saat pengguna mencoba berinteraksi dengan halaman, sehingga menunda interaksi.
Bagaimanapun, 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 tahap terakhir dari interaksi, dan dimulai saat pengendali peristiwa interaksi selesai, hingga titik saat frame berikutnya dirender. Perubahan ini terjadi saat pekerjaan dalam 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 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 beberapa penyebab yang perlu diwaspadai.
Pekerjaan gaya dan tata letak yang mahal
Penundaan presentasi yang lama dapat berupa pekerjaan penghitungan ulang gaya dan tata letak yang mahal yang timbul 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.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 sebuah 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 akhir frame, dan mengurangi stempel waktu mulai pekerjaan gaya dan tata letak dari waktu tersebut.
Callback requestAnimationFrame yang berjalan lama
Salah satu kemungkinan 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 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 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.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 update aktual pada antarmuka pengguna. Pekerjaan lain yang tidak menyentuh DOM atau memperbarui gaya akan menunda rendering frame berikutnya tanpa alasan, 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 yang bermasalah di lab, lalu memperbaikinya.