Mengukur analisis dan telemetri sesi penuh dengan andal

Untuk melacak analisis, Core Web Vitals, dan data telemetri yang mencakup seluruh kunjungan pengguna ke halaman web (tidak hanya hingga pemuatan halaman) dengan andal, gunakan fetchLater() API.

fetchLater() API adalah cara paling andal untuk mengirim data ke server dalam kasus saat respons tidak diperlukan dan waktu pengiriman tidak mendesak, yang berlaku untuk data seperti analisis pengguna, telemetri, pelacakan error, dan metrik performa seperti Core Web Vitals.

Teknik lama seperti membuat piksel <img> di pemroses peristiwa unload terkenal tidak dapat diandalkan (terutama di perangkat seluler) dan dapat berdampak negatif pada performa (dengan membuat halaman tidak memenuhi syarat untuk bfcache).

Cara menerapkan

  1. Jadwalkan permintaan: Segera setelah data yang relevan tersedia, panggil fetchLater() dengan payload data Anda. Tindakan ini akan mengantrekan data untuk dikirim nanti.

  2. Perbarui payload sesuai kebutuhan: Jika pengguna menghasilkan lebih banyak data atau status berubah, batalkan permintaan yang dijadwalkan sebelumnya dan panggil fetchLater() lagi dengan snapshot yang telah diperbarui sepenuhnya.

  3. Biarkan browser menangani sisanya: Saat pengguna keluar atau menutup tab, browser akan mengirimkan payload dari panggilan fetchLater() terbaru Anda dengan andal.

Kode contoh

Kode ini mengukur durasi sesi kunjungan pengguna ke halaman menggunakan fetchLater() untuk mengantrekan beacon baru setiap 10 detik dengan durasi sesi yang diperbarui.

const ANALYTICS_ENDPOINT = '/path/to/analytics/endpoint';

const sessionData = {
  duration: 0,
  id: crypto.randomUUID(),
};

let fetchLaterController = null;

function queueBeacon() {
  // Abort any pending beacons before creating a new one.
  if (fetchLaterController) {
    fetchLaterController.abort();
  }
  fetchLaterController = new AbortController();

  // Update session duration to the current page time.
  sessionData.duration = performance.now();

  // Schedule a fetch for the data payload to be sent later.
  // IMPORTANT: wrap the call in a try/catch to handle quota errors.
  try {
    fetchLater(ANALYTICS_ENDPOINT, {
      method: 'POST',
      headers: {'content-type': 'application/json'},
      body: JSON.stringify(sessionData),
      signal: fetchLaterController.signal,
    });
  } catch (error) {
    // Handle errors as needed.
  }
}

// Update the session data and queue a new beacon every 10 seconds.
setInterval(queueBeacon, 10000);

Praktik Terbaik

  • LAKUKAN penggunaan fetchLater() untuk mengirim data ke server dalam situasi apa pun saat respons tidak diperlukan, dan data tidak harus segera dikirim.
  • LAKUKAN penggunaan AbortController untuk membatalkan pengambilan yang tertunda jika data mungkin perlu diperbarui sebelum pengguna keluar dari halaman
  • LAKUKAN pengurangan ukuran payload untuk menghindari melebihi kuota (saat ini sekitar 64 KB per origin).
  • LAKUKAN penggabungan panggilan ke fetchLater() dalam try/catch untuk menangani error kuota.
  • LAKUKAN deteksi fitur keberadaan fetchLater() di globalThis dan terapkan strategi penggantian untuk browser yang tidak mendukung API.
  • JANGAN gunakan objek ReadableStream untuk isi permintaan, karena akan menyebabkan error.

Dukungan browser dan strategi penggantian

Strategi penggantian diperlukan jika fetchLater() tidak memenuhi target Dasar pengukuran Anda. Namun, mengingat manfaat peningkatan keandalan dan performa API ini, fetchLater() harus digunakan jika browser mendukungnya.

Strategi penggantian yang direkomendasikan adalah menggunakan polyfill di bawah, yang secara internal menangani browser yang tidak mendukung menggunakan fetch() dengan keepalive atau navigator.sendBeacon(). Kode Anda HARUS memanggil fetchLater() secara langsung — jangan pernah memanggil fetch(), sendBeacon(), atau API beacon lainnya sendiri.

fetchLater() polyfill

Gunakan polyfill fetchLater() minimal berikut, yang mengimplementasikan API semirip mungkin di browser yang tidak mendukung.

Satu-satunya perbedaan perilaku yang terlihat dengan polyfill ini adalah penggunaan visibilitychange untuk mendeteksi saat pengguna keluar, bukan mengandalkan penanganan unload native browser. Ini adalah detail implementasi internal — kode Anda tidak perlu memproses visibilitychange atau peristiwa siklus proses halaman lainnya. Cukup panggil fetchLater() dan polyfill akan menangani pengiriman.

globalThis.fetchLater ??= function fetchLater(url, init = {}) {
  let timeoutHandle;
  let activated = false;

  function sendNow() {
    if (!(init.signal && init.signal.aborted)) {
      // Use fetch keepalive if the browser supports it or if custom fetch
      // parameters are specified (e.g. custom headers or methods).
      // Otherwise fall back to `navigator.sendBeacon()`.
      if (
        'keepalive' in Request.prototype ||
        init.method !== 'POST' ||
        init.headers
      ) {
        fetch(url, Object.assign({}, init, {keepalive: true}));
        activated = true;
      } else {
        activated = navigator.sendBeacon(url, init.body);
      }
    }
    destroy();
  }

  function destroy() {
    document.removeEventListener('visibilitychange', sendNow);
    clearTimeout(timeoutHandle);
  }

  if (document.visibilityState === 'hidden') {
    // If the beacon was created while the page is already hidden, send data
    // ASAP but wait until the next microtask to allow all sync code to run.
    queueMicrotask(sendNow);
  } else {
    document.addEventListener('visibilitychange', sendNow);

    if (typeof init.activateAfter === 'number' && init.activateAfter >= 0) {
      timeoutHandle = setTimeout(sendNow, init.activateAfter);
    }
  }

  if (init.signal) {
    init.signal.addEventListener('abort', destroy);
  }

  return {
    get activated() {
      return activated;
    },
  };
};