Metrik kustom

Ada banyak manfaat dari memiliki metrik yang berfokus pada pengguna yang dapat Anda ukur, secara universal, di situs mana pun. Metrik ini memungkinkan Anda:

  • Memahami bagaimana pengguna yang sebenarnya menggunakan web secara keseluruhan.
  • Bandingkan situs Anda dengan situs pesaing.
  • Lacak data yang berguna dan bisa ditindaklanjuti di alat analisis Anda tanpa perlu menulis kode kustom.

Metrik universal menawarkan dasar pengukuran yang baik, tetapi dalam banyak kasus, Anda perlu mengukur lebih dari metrik ini untuk mendapatkan pengalaman lengkap untuk situs Anda.

Metrik kustom memungkinkan Anda mengukur aspek pengalaman situs yang mungkin hanya berlaku untuk situs Anda, seperti:

  • Waktu yang diperlukan aplikasi satu halaman (SPA) untuk bertransisi dari satu "halaman" ke halaman lainnya.
  • Waktu yang diperlukan halaman untuk menampilkan data yang diambil dari database untuk pengguna yang login.
  • Waktu yang diperlukan aplikasi yang dirender sisi server (SSR) untuk menghidrasi.
  • Rasio cache ditemukan untuk resource yang dimuat oleh pengunjung yang kembali.
  • Latensi peristiwa klik atau peristiwa keyboard dalam game.

API untuk mengukur metrik kustom

Secara historis, developer web tidak memiliki banyak API tingkat rendah untuk mengukur performa, dan akibatnya mereka harus menggunakan hack untuk mengukur apakah situs berperforma baik.

Misalnya, Anda dapat menentukan apakah thread utama diblokir karena tugas JavaScript yang berjalan lama dengan menjalankan loop requestAnimationFrame dan menghitung delta di antara setiap frame. Jika delta jauh lebih lama daripada kecepatan frame layar, Anda dapat melaporkannya sebagai tugas yang lama. Namun, hack semacam itu tidak direkomendasikan karena sebenarnya hack tersebut memengaruhi performa (misalnya, dengan menghabiskan baterai).

Aturan pertama pengukuran performa yang efektif adalah memastikan teknik pengukuran performa Anda tidak menyebabkan masalah performa itu sendiri. Jadi, untuk metrik kustom apa pun yang Anda ukur di situs, sebaiknya gunakan salah satu API berikut jika memungkinkan.

Performance Observer API

Dukungan Browser

  • Chrome: 52.
  • Edge: 79.
  • Firefox: 57.
  • Safari: 11.

Sumber

Performance Observer API adalah mekanisme yang mengumpulkan dan menampilkan data dari semua API performa lainnya yang dibahas di halaman ini. Memahaminya sangat penting untuk mendapatkan data yang baik.

Anda dapat menggunakan PerformanceObserver untuk berlangganan peristiwa terkait performa secara pasif. Hal ini memungkinkan callback API diaktifkan selama periode tidak ada aktivitas, yang berarti callback ini biasanya tidak akan mengganggu performa halaman.

Untuk membuat PerformanceObserver, teruskan callback yang akan dijalankan setiap kali entri performa baru dikirim. Kemudian, Anda memberi tahu observer jenis entri yang akan diproses menggunakan metode observe():

const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // Log the entry and all associated details.
    console.log(entry.toJSON());
  }
});

po.observe({type: 'some-entry-type'});

Bagian berikut mencantumkan semua jenis entri yang tersedia untuk diamati, tetapi di browser yang lebih baru, Anda dapat memeriksa jenis entri yang tersedia melalui properti PerformanceObserver.supportedEntryTypes statis.

Mengamati entri yang sudah terjadi

Secara default, objek PerformanceObserver hanya dapat mengamati entri saat terjadi. Hal ini dapat menyebabkan masalah jika Anda ingin memuat lambat kode analisis performa agar tidak memblokir resource dengan prioritas lebih tinggi.

Untuk mendapatkan entri historis (setelah terjadi), tetapkan tanda buffered ke true saat Anda memanggil observe(). Browser akan menyertakan entri historis dari buffer entri performa saat pertama kali callback PerformanceObserver Anda dipanggil, hingga ukuran buffer maksimum untuk jenis tersebut.

po.observe({
  type: 'some-entry-type',
  buffered: true,
});

API performa lama yang harus dihindari

Sebelum Performance Observer API, developer dapat mengakses entri performa menggunakan tiga metode berikut yang ditentukan pada objek performance:

Meskipun API ini masih didukung, penggunaannya tidak direkomendasikan karena tidak memungkinkan Anda memproses saat entri baru dimunculkan. Selain itu, banyak API baru (seperti largest-contentful-paint) tidak diekspos melalui objek performance, tetapi hanya diekspos melalui PerformanceObserver.

Kecuali jika Anda secara khusus memerlukan kompatibilitas dengan Internet Explorer, sebaiknya hindari metode ini dalam kode Anda dan gunakan PerformanceObserver ke depannya.

API Waktu Pengguna

Dukungan Browser

  • Chrome: 28.
  • Edge: 12.
  • Firefox: 38.
  • Safari: 11.

Sumber

User Timing API adalah API pengukuran tujuan umum untuk metrik berbasis waktu. Dengan ini, Anda dapat menandai titik waktu secara arbitrer, lalu mengukur durasi di antara tanda tersebut nanti.

// Record the time immediately before running a task.
performance.mark('myTask:start');
await doMyTask();

// Record the time immediately after running a task.
performance.mark('myTask:end');

// Measure the delta between the start and end of the task
performance.measure('myTask', 'myTask:start', 'myTask:end');

Meskipun API seperti Date.now() atau performance.now() memberi Anda kemampuan serupa, manfaat menggunakan User Timing API adalah API ini terintegrasi dengan baik dengan alat performa. Misalnya, Chrome DevTools memvisualisasikan pengukuran Waktu Pengguna di panel Performa, dan banyak penyedia analisis juga akan otomatis melacak pengukuran apa pun yang Anda buat dan mengirim data durasi ke backend analisis mereka.

Untuk melaporkan pengukuran Waktu Pengguna, Anda dapat menggunakan PerformanceObserver dan mendaftar untuk mengamati entri jenis measure:

// Create the performance observer.
const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // Log the entry and all associated details.
    console.log(entry.toJSON());
  }
});

// Start listening for `measure` entries to be dispatched.
po.observe({type: 'measure', buffered: true});

Long Tasks API

Dukungan Browser

  • Chrome: 58.
  • Edge: 79.
  • Firefox: tidak didukung.
  • Safari: tidak didukung.

Sumber

Long Tasks API berguna untuk mengetahui kapan thread utama browser diblokir cukup lama untuk memengaruhi kecepatan frame atau latensi input. API akan melaporkan setiap tugas yang dieksekusi selama lebih dari 50 milidetik.

Setiap kali Anda perlu menjalankan kode yang mahal, atau memuat dan mengeksekusi skrip besar, sebaiknya lacak apakah kode tersebut memblokir thread utama. Faktanya, banyak metrik tingkat tinggi yang dibuat berdasarkan Long Tasks API itu sendiri (seperti Time to Interactive (TTI) dan Total Blocking Time (TBT)).

Untuk menentukan kapan tugas yang berjalan lama terjadi, Anda dapat menggunakan PerformanceObserver dan mendaftar untuk mengamati entri jenis longtask:

// Create the performance observer.
const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // Log the entry and all associated details.
    console.log(entry.toJSON());
  }
});

// Start listening for `longtask` entries to be dispatched.
po.observe({type: 'longtask', buffered: true});

Long Animation Frames API

Dukungan Browser

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

Sumber

Long Animation Frames API adalah iterasi baru dari Long Tasks API yang melihat frame panjang—bukan tugas panjang—yang berdurasi lebih dari 50 milidetik. Hal ini mengatasi beberapa kelemahan Long Tasks API, termasuk atribusi yang lebih baik dan cakupan penundaan yang berpotensi bermasalah yang lebih luas.

Untuk menentukan kapan frame panjang terjadi, Anda dapat menggunakan PerformanceObserver dan mendaftar untuk mengamati entri jenis long-animation-frame:

// Create the performance observer.
const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // Log the entry and all associated details.
    console.log(entry.toJSON());
  }
});

// Start listening for `long-animation-frame` entries to be dispatched.
po.observe({type: 'long-animation-frame', buffered: true});

Element Timing API

Dukungan Browser

  • Chrome: 77.
  • Edge: 79.
  • Firefox: tidak didukung.
  • Safari: tidak didukung.

Sumber

Metrik Largest Contentful Paint (LCP) berguna untuk mengetahui kapan gambar atau blok teks terbesar di-paint ke layar, tetapi dalam beberapa kasus, Anda ingin mengukur waktu render elemen yang berbeda.

Untuk kasus ini, gunakan Element Timing API. LCP API sebenarnya dibuat di atas Element Timing API dan menambahkan pelaporan otomatis dari elemen konten terbesar, tetapi Anda juga dapat melaporkan elemen lain dengan secara eksplisit menambahkan atribut elementtiming ke elemen tersebut, dan mendaftarkan PerformanceObserver untuk mengamati jenis entri element.

<img elementtiming="hero-image" />
<p elementtiming="important-paragraph">This is text I care about.</p>
<!-- ... -->

<script>
  const po = new PerformanceObserver((entryList) => {
    for (const entry of entryList.getEntries()) {
      // Log the entry and all associated details.
      console.log(entry.toJSON());
    }
  });

  // Start listening for `element` entries to be dispatched.
  po.observe({type: 'element', buffered: true});
</script>

Event Timing API

Dukungan Browser

  • Chrome: 76.
  • Edge: 79.
  • Firefox: 89.
  • Safari: tidak didukung.

Sumber

Metrik Interaction to Next Paint (INP) menilai responsivitas halaman secara keseluruhan dengan mengamati semua interaksi klik, ketukan, dan keyboard selama masa aktif halaman. INP halaman sering kali merupakan interaksi yang memerlukan waktu paling lama untuk diselesaikan, mulai dari saat pengguna memulai interaksi, hingga saat browser menggambar frame berikutnya yang menunjukkan hasil visual dari input pengguna.

Metrik INP dimungkinkan oleh Event Timing API. API ini mengekspos sejumlah stempel waktu yang terjadi selama siklus proses peristiwa, termasuk:

  • startTime: waktu saat browser menerima peristiwa.
  • processingStart: waktu saat browser dapat mulai memproses pengendali peristiwa untuk peristiwa tersebut.
  • processingEnd: waktu saat browser selesai mengeksekusi semua kode sinkron yang dimulai dari pengendali peristiwa untuk peristiwa ini.
  • duration: waktu (dibulatkan menjadi 8 milidetik karena alasan keamanan) antara saat browser menerima peristiwa hingga dapat menggambar frame berikutnya setelah selesai menjalankan semua kode sinkron yang dimulai dari pengendali peristiwa.

Contoh berikut menunjukkan cara menggunakan nilai ini untuk membuat pengukuran kustom:

const po = new PerformanceObserver((entryList) => {
  // Get the last interaction observed:
  const entries = Array.from(entryList.getEntries()).forEach((entry) => {
    // Get various bits of interaction data:
    const inputDelay = entry.processingStart - entry.startTime;
    const processingTime = entry.processingEnd - entry.processingStart;
    const presentationDelay = entry.startTime + entry.duration - entry.processingEnd;
    const duration = entry.duration;
    const eventType = entry.name;
    const target = entry.target || "(not set)"

    console.log("----- INTERACTION -----");
    console.log(`Input delay (ms): ${inputDelay}`);
    console.log(`Event handler processing time (ms): ${processingTime}`);
    console.log(`Presentation delay (ms): ${presentationDelay}`);
    console.log(`Total event duration (ms): ${duration}`);
    console.log(`Event type: ${eventType}`);
    console.log(target);
  });
});

// A durationThreshold of 16ms is necessary to include more
// interactions, since the default is 104ms. The minimum
// durationThreshold is 16ms.
po.observe({type: 'event', buffered: true, durationThreshold: 16});

Resource Timing API

Dukungan Browser

  • Chrome: 29.
  • Edge: 12.
  • Firefox: 35.
  • Safari: 11.

Sumber

Resource Timing API memberi developer insight mendetail tentang cara resource untuk halaman tertentu dimuat. Terlepas dari nama API ini, informasi yang diberikannya tidak hanya terbatas pada data waktu (meskipun ada banyak sekali). Data lain yang dapat Anda akses mencakup:

  • initiatorType: cara resource diambil: seperti dari tag <script> atau <link>, atau dari panggilan fetch().
  • nextHopProtocol: protokol yang digunakan untuk mengambil resource, seperti h2 atau quic.
  • encodedBodySize/decodedBodySize]: ukuran resource dalam bentuk yang dienkode atau didekode (masing-masing)
  • transferSize: ukuran resource yang benar-benar ditransfer melalui jaringan. Jika resource dipenuhi oleh cache, nilai ini dapat jauh lebih kecil daripada encodedBodySize, dan dalam beberapa kasus dapat nol (jika tidak diperlukan validasi ulang cache).

Anda dapat menggunakan properti transferSize dari entri waktu resource untuk mengukur metrik rasio hit cache atau metrik total ukuran resource yang di-cache, yang dapat berguna dalam memahami pengaruh strategi penyimpanan dalam cache resource Anda terhadap performa untuk pengunjung berulang.

Contoh berikut mencatat semua resource yang diminta oleh halaman dan menunjukkan apakah setiap resource dipenuhi oleh cache atau tidak.

// Create the performance observer.
const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // If transferSize is 0, the resource was fulfilled using the cache.
    console.log(entry.name, entry.transferSize === 0);
  }
});

// Start listening for `resource` entries to be dispatched.
po.observe({type: 'resource', buffered: true});

Dukungan Browser

  • Chrome: 57.
  • Edge: 12.
  • Firefox: 58.
  • Safari: 15.

Sumber

Navigation Timing API mirip dengan Resource Timing API, tetapi hanya melaporkan permintaan navigasi. Jenis entri navigation juga mirip dengan jenis entri resource, tetapi berisi beberapa informasi tambahan khusus untuk permintaan navigasi saja (seperti saat peristiwa DOMContentLoaded dan load diaktifkan).

Satu metrik yang dilacak banyak developer untuk memahami waktu respons server (Time to First Byte (TTFB)) tersedia menggunakan Navigation Timing API—khususnya stempel waktu responseStart entri.

// Create the performance observer.
const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // If transferSize is 0, the resource was fulfilled using the cache.
    console.log('Time to first byte', entry.responseStart);
  }
});

// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});

Metrik lain yang mungkin penting bagi developer yang menggunakan pekerja layanan adalah waktu startup pekerja layanan untuk permintaan navigasi. Ini adalah jumlah waktu yang diperlukan browser untuk memulai thread pekerja layanan sebelum dapat mulai mencegat peristiwa pengambilan.

Waktu startup pekerja layanan untuk permintaan navigasi tertentu dapat ditentukan dari delta antara entry.responseStart dan entry.workerStart.

// Create the performance observer.
const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log('Service Worker startup time:',
        entry.responseStart - entry.workerStart);
  }
});

// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});

API Waktu Server

Dukungan Browser

  • Chrome: 65.
  • Edge: 79.
  • Firefox: 61.
  • Safari: 16.4.

Sumber

Server Timing API memungkinkan Anda meneruskan data waktu khusus permintaan dari server Anda ke browser melalui header respons. Misalnya, Anda dapat menunjukkan waktu yang diperlukan untuk mencari data dalam database untuk permintaan tertentu—yang dapat berguna dalam proses debug masalah performa yang disebabkan oleh kelambatan di server.

Untuk developer yang menggunakan penyedia analisis pihak ketiga, Server Timing API adalah satu-satunya cara untuk mengaitkan data performa server dengan metrik bisnis lainnya yang mungkin diukur oleh alat analisis ini.

Untuk menentukan data pengaturan waktu server dalam respons, Anda dapat menggunakan header respons Server-Timing. Berikut contohnya.

HTTP/1.1 200 OK

Server-Timing: miss, db;dur=53, app;dur=47.2

Kemudian, dari halaman, Anda dapat membaca data ini pada entri resource atau navigation dari Resource Timing dan Navigation Timing API.

// Create the performance observer.
const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // Logs all server timing data for this response
    console.log('Server Timing', entry.serverTiming);
  }
});

// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});