Pelajari cara menggunakan API ini untuk mengukur kinerja aplikasi Anda

Browser DevTools sangat bagus untuk memantau kinerja aplikasi web di PC pengembangan lokal Anda. Namun, cara ini kurang praktis untuk mengukur kecepatan situs di berbagai perangkat, browser, dan koneksi jaringan di seluruh lokasi global.

Performance API mencatat metrik seperti DevTool dari pengguna sebenarnya saat mereka menavigasi aplikasi Anda. Anda dapat memposting data yang dikumpulkan ke layanan seperti Asayer.io, mis.

asayer.event('load-performance', {
'os'      : 'Ubuntu',
  'agent'   : 'Firefox 88.0',
  'location': 'US',
  'pageload': 1522.598,
  'paint'   : 5969.123,
  'ajaxinit': 1507.067
});

Untuk membantu mengidentifikasi hambatan kinerja pada browser, perangkat, atau bahkan sesi pengguna tertentu.

Apa itu API Kinerja?

Performance API adalah kumpulan API yang digunakan untuk mengukur:

Secara historis, pengembang harus mengadopsi fungsi Date() untuk mencatat waktu yang telah berlalu, misalnya.

const startTime = new Date();
doSomething();
const elapsedTime = new Date() - startTime;
console.log(`doSomething() took ${ elapsedTime }ms`);

tetapi Performance API-nya adalah:

  1. resolusi lebih tinggi. Berbeda dengan Date(), ini mencatat waktu dalam sepersekian milidetik.
  2. lebih terpercaya. Date() menggunakan waktu sistem sehingga pengaturan waktu menjadi tidak akurat ketika OS menyinkronkan jam.

API tersedia dalam JavaScript sisi klien di sebagian besar browser modern dan dapat dideteksi dengan:

if ('performance' in window) {
  // use Performance APIs
}

Sumber daya dan waktu pengguna juga tersedia di Pekerja Web sisi klien. Ini memberikan cara untuk menjalankan skrip yang berjalan lama atau mahal secara komputasi di latar belakang yang tidak mengganggu thread pemrosesan utama browser.

API pengaturan waktu pengguna juga tersedia di sisi server:

  • Aplikasi Node.js dengan modul performance_hook, dan
  • Aplikasi Deno dijalankan dengan opsi --allow-hrtime.

Performance API dan dokumentasinya mungkin agak sulit dipahami karena telah berkembang. Saya harap informasi dan contoh dalam artikel ini membantu menggambarkan potensinya.

Memuat properti waktu

Bagian di bawah ini menjelaskan:

  1. waktu navigasi halaman yang mengembalikan objek PerformanceNavigationTiming, dan
  2. resource timings yang mengembalikan objek PerformanceResourceTiming

Kedua objek menyediakan properti identifikasi berikut:

  • nama — URL sumber daya
  • entryType — jenis kinerja ("navigation" untuk laman, "resource" untuk aset laman)
  • initiatorType — sumber daya yang memulai entri kinerja ("navigation" untuk laman)
  • serverTiming — array objek PerformanceServerTiming dengan metrik name, description, dan duration yang ditulis oleh server ke HTTP Server-Timing header

Kedua objek menyediakan properti pengaturan waktu berikut yang ditampilkan di sini dalam urutan kronologis yang Anda perkirakan akan terjadi. Stempel waktu dalam milidetik relatif terhadap awal pemuatan halaman:

  • startTime — stempel waktu saat pengambilan dimulai (0 untuk laman karena ini adalah aset pertama yang dimuat)
  • nextHopProtocol — protokol jaringan yang digunakan
  • workerStart — stempel waktu sebelum memulai Service Worker Aplikasi Web Progresif (PWA) (0 jika permintaan tidak dicegat oleh Service Worker)
  • redirectStart — stempel waktu pengambilan yang memulai pengalihan
  • redirectEnd — stempel waktu setelah menerima byte terakhir dari respons pengalihan terakhir
  • fetchStart — stempel waktu sebelum sumber daya diambil
  • domainLookupStart — stempel waktu sebelum pencarian DNS
  • domainLookupEnd — stempel waktu setelah pencarian DNS
  • connectStart — stempel waktu sebelum browser membuat koneksi server
  • connectEnd — stempel waktu setelah membuat koneksi server
  • secureConnectionStart — stempel waktu sebelum browser memulai proses jabat tangan SSL
  • requestStart — stempel waktu sebelum browser meminta sumber daya
  • responseStart — stempel waktu saat browser menerima byte data pertama
  • responseEnd — stempel waktu setelah menerima byte terakhir atau menutup koneksi
  • durasi — perbedaan antara startTime dan responseEnd

Kedua objek menyediakan properti ukuran unduhan berikut:

  • transferSize — ukuran sumber daya dalam byte (oktet), termasuk header dan isi kompresi
  • encodedBodySize — isi muatan sumber daya dalam byte (oktet) sebelum mendekode/membuka kompresi
  • decodedBodySize — isi payload sumber daya dalam byte (oktet) setelah decoding/pembukaan kompresi

Objek halaman PerformanceNavigationTiming memberikan metrik lebih lanjut tentang pemuatan dan peristiwa DOM, meskipun ini tidak didukung di Safari:

  • redirectCount — jumlah pengalihan
  • unloadEventStart — stempel waktu sebelum peristiwa unload pada dokumen sebelumnya (nol jika tidak ada dokumen sebelumnya)
  • unloadEventEnd — stempel waktu setelah peristiwa unload pada dokumen sebelumnya (nol jika tidak ada dokumen sebelumnya)
  • domInteractive — stempel waktu sebelum browser menyetel kesiapan dokumen ke interaktif ketika penguraian HTML dan konstruksi DOM selesai
  • domContentLoadedEventStart — stempel waktu sebelum peristiwa DOMContentLoaded dokumen diaktifkan
  • domContentLoadedEventEnd — stempel waktu setelah acara DOMContentLoaded dokumen selesai
  • domComplete — stempel waktu sebelum browser menetapkan kesiapan dokumen untuk selesai ketika konstruksi DOM dan peristiwa DOMContentLoaded telah selesai
  • loadEventStart — stempel waktu sebelum peristiwa laman load diaktifkan
  • loadEventEnd — stempel waktu setelah peristiwa laman load dan semua aset tersedia

Waktu navigasi

Navigation Timing API menyusun waktu untuk membongkar halaman sebelumnya, pengalihan, pencarian DNS, pemuatan halaman, ukuran file, peristiwa pemuatan, dan banyak lagi. Informasi tersebut akan sulit ditentukan secara andal dengan cara lain apa pun.

Pengaturan waktu navigasi tersedia untuk jendela JavaScript sisi klien dan fungsi Web Worker. Berikan tipe "navigation" ke performance.getEntriesByType():

const pageTiming = performance.getEntriesByType('navigation');

atau URL halaman ke performance.getEntriesByName():

const pageTiming = performance.getEntriesByName(window.location);

Opsi mana pun akan mengembalikan array dengan elemen tunggal yang berisi objek PerformanceNavigationTiming (lihat properti waktu muat). Ini berisi properti read-only tentang waktu pemuatan sumber daya, mis.

{
  connectEnd: 139
  connectStart: 103
  decodedBodySize: 72325
  domComplete: 771
  domContentLoadedEventEnd: 634
  domContentLoadedEventStart: 630
  domInteractive: 421
  domainLookupEnd: 103
  domainLookupStart: 87
  duration: 771
  encodedBodySize: 13091
  entryType: "navigation"
  fetchStart: 0
  initiatorType: "navigation"
  loadEventEnd: 771
  loadEventStart: 771
  name: "https://domain.com/"
  nextHopProtocol: "h2"
  redirectCount: 0
  redirectEnd: 0
  redirectStart: 0
  requestStart: 140
  responseEnd: 154
  responseStart: 154
  secureConnectionStart: 115
  serverTiming: Array []
  startTime: 0
  transferSize: 13735
  type: "reload"
  unloadEventEnd: 171
  unloadEventStart: 169
  workerStart: 0
}

Anda dapat menggunakannya untuk menghitung metrik pemuatan halaman yang berguna dari pengguna, misalnya.

if ('performance' in window) {
const
    pageTiming      = performance.getEntriesByName(window.location)[0],
    pageDownload    = pageTiming.duration,
    pageDomReady    = pageTiming.domContentLoadedEventStart,
    pageFullyReady  = pageTiming.loadEventEnd;
}

Waktu sumber daya

Anda dapat memeriksa waktu pemuatan untuk sumber daya lain seperti gambar, stylesheet, skrip, Fetch, dan XMLHttpRequest panggilan Ajax dengan cara yang mirip dengan halaman.

Pengaturan waktu sumber daya tersedia untuk jendela JavaScript sisi klien dan fungsi Web Worker. Berikan tipe "resource" ke performance.getEntriesByType() untuk mengembalikan array. Setiap elemen adalah objek PerformanceResourceTiming (lihat properti waktu muat) yang mewakili sumber daya yang dimuat oleh halaman (tetapi bukan halaman itu sendiri):

const resourceTiming = performance.getEntriesByType('resource');

contoh hasil:

[
  {
    name: "https://domain.com/script1.js",
    entryType: "resource",
    initiatorType: "script",
    fetchStart: 102,
    duration: 51
    ...etc...
  },
  {
    name: "https://domain.com/style1.css",
    entryType: "resource",
    initiatorType: "link",
    fetchStart: 323,
    duration: 54
    ...etc...
  },
  {
    name: "https://domain.com/service/",
    entryType: "resource",
    initiatorType: "xmlhttprequest",
    fetchStart: 598,
    duration: 30
    ...etc...
  },
  ...etc...
]

Anda juga dapat mengambil sumber daya dengan meneruskan URL tepat ke performance.getEntriesByName():

const resourceTiming = performance.getEntriesByName('https://domain.com/style1.css');

Ini mengembalikan array dengan satu elemen:

[
  {
    name: "https://domain.com/style1.css",
    entryType: "resource",
    initiatorType: "link",
    fetchStart: 323,
    duration: 54
    ...etc...
  }
]

Anda dapat menggunakan ini untuk melaporkan guna menghitung waktu muat dan ukuran setiap sumber daya JavaScript serta totalnya:

if ('performance' in window) {
  // total size of all JavaScript files
  let scriptTotalSize = 0;
  // array of script names, load times, and uncompressed file sizes
  const script = performance.getEntriesByType('resource')
    .filter( r => r.initiatorType === 'script')
    .map( r => {
  let size = r.decodedBodySize;
    scriptTotalSize += size;
  return {
    name: r.name,
    load: r.duration,
    size
   };
  });
}

Performance API mencatat setidaknya 150 metrik sumber daya, namun Anda dapat menentukan nomor tertentu dengan performance.setResourceTimingBufferSize(N), misalnya.

// record metrics for 300 page resources
performance.setResourceTimingBufferSize(300);

Anda dapat menghapus metrik yang ada dengan performance.clearResourceTimings(). Ini mungkin praktis ketika Anda tidak lagi memerlukan informasi sumber daya halaman tetapi ingin mencatat permintaan Ajax:

// clear timings
performance.clearResourceTimings();
// API Fetch request
const res = await Fetch('/service1/');
// one resource returned
const resourceTiming = performance.getEntriesByType('resource');

Waktu pengecatan

Paint Timing API tersedia untuk fungsi jendela JavaScript sisi klien dan mencatat dua operasi rendering yang diamati selama konstruksi halaman.

Berikan tipe "paint" ke performance.getEntriesByType() untuk mengembalikan array yang berisi dua objek PerformancePaintTiming:

const paintTiming = performance.getEntriesByType('paint');

Hasil:

[
  {
    "name": "first-paint",
    "entryType": "paint",
    "startTime": 242,
    "duration": 0
  },
  {
    "name": "first-contentful-paint",
    "entryType": "paint",
    "startTime": 243,
    "duration": 0
  }
]

Di mana:

  • first-paint: browser telah melukis piksel pertama pada laman, dan
  • first-contentful-paint: browser telah mengecat item pertama konten DOM, seperti teks atau gambar.

Perhatikan bahwa "duration" akan selalu nol.

kinerja.sekarang()

performance.now() mengembalikan stempel waktu resolusi tinggi dalam sepersekian milidetik sejak awal masa proses. Metode ini tersedia di JavaScript sisi klien, Pekerja Web, Node.js, dan Deno.

Di JavaScript sisi klien, pengatur waktu performance.now() dimulai dari nol ketika proses yang bertanggung jawab untuk membuat document dimulai. Timer Web Worker, Node.js, dan Deno dimulai saat proses skrip pertama kali dijalankan.

Perhatikan bahwa skrip Node.js harus memuat Modul Performance hooks (perf_hooks) untuk menggunakan Performance API. Di CommonJS:

const { performance } = require('perf_hooks');

atau sebagai modul ES:

import { performance } from 'perf_hooks';

Anda dapat menggunakan performance.now() untuk skrip waktu, mis.

const doSomethingStart = performance.now();
doSomething();
const doSomethingElapsed = performance.now() - doSomethingStart;

"PropertitimeOrigin" non-standar selanjutnya mengembalikan stempel waktu saat proses saat ini dimulai. Ini diukur dalam waktu Unix sejak 1 Januari 1970 dan tersedia di Node.js dan browser JavaScript (bukan IE atau Safari):

Waktu pengguna

performance.now() menjadi rumit saat melakukan lebih dari beberapa pengukuran waktu. Metode "performance.mark()" menambahkan objek bernama "Objek PerformanceMark" dengan stempel waktu ke buffer kinerja. Ini tersedia di JavaScript sisi klien, Pekerja Web, Node.js, dan Deno:

// Node.js scripts require:
// CommonJS: const { performance } = require('perf_hooks');
// or ESM  : import { performance } from 'perf_hooks';
performance.mark('script:start');
performance.mark('doSomething1:start');
doSomething1();
performance.mark('doSomething1:end');
performance.mark('doSomething2:start');
doSomething2();
performance.mark('doSomething2:end');
performance.mark('script:end');

Berikan tipe "mark" ke performance.getEntriesByType() untuk mengembalikan serangkaian tanda:

const userTiming = performance.getEntriesByType('mark');

Array yang dihasilkan berisi objek dengan properti name dan startTime:

[
  {
    detail: null
    duration: 0
    entryType: "mark"
    name: "script:start"
    startTime: 100
  },
  {
    detail: null
    duration: 0
    entryType: "mark"
    name: "doSomething1:start"
    startTime: 100
  },
  {
    detail: null
    duration: 0
    entryType: "mark"
    name: "doSomething1:end"
    startTime: 123
  },
  ...etc...
]

Metode performance.measure() menghitung waktu yang berlalu antara dua tanda. Itu melewati nama ukuran, nama tanda awal (atau nilai palsu untuk menggunakan waktu buka halaman/skrip), dan nama tanda akhir (atau nilai palsu untuk menggunakan waktu saat ini), misalnya.

performance.measure('doSomething1', 'doSomething1:start', 'doSomething1:end');
performance.measure('script', null, 'doSomething1:end');

Ini menambahkan objek PerformanceMeasure ke buffer kinerja dengan durasi yang dihitung. Berikan tipe "measure" ke performance.getEntriesByType() untuk mengembalikan serangkaian ukuran:

const userTiming = performance.getEntriesByType('measure');

Array yang dihasilkan:

[
  {
    detail: null
    duration: 211
    entryType: "measure"
    name: "doSomething1"
    startTime: 100
  },
  {
    detail: null
    duration: 551
    entryType: "measure"
    name: "script"
    startTime: 100
  }
]

Anda juga dapat mengambil entri tanda dan ukur berdasarkan nama menggunakan performance.getEntriesByName():

performance.getEntriesByName('doSomething1');

Metode bermanfaat lainnya meliputi:

Pemantauan Bagian Depan

Asayer adalah alat pemantauan frontend yang memutar ulang semua yang dilakukan pengguna Anda dan menunjukkan bagaimana aplikasi web Anda berperilaku untuk setiap masalah. Ini memungkinkan Anda mereproduksi masalah, menggabungkan kesalahan JS, dan memantau kinerja aplikasi web Anda.

Selamat melakukan debug, untuk tim frontend modern — Mulai pantau aplikasi web Anda secara gratis.

Pengamat Kinerja

Antarmuka PerformanceObserver dapat melihat perubahan pada buffer kinerja dan menjalankan fungsi ketika objek tertentu muncul. Ini paling praktis digunakan untuk peristiwa penandaan, pengukuran, dan pemuatan sumber daya (waktu navigasi dan pengecatan umumnya akan terjadi sebelum skrip dimulai).

Pertama, tentukan fungsi pengamat. Ini dapat mencatat peristiwa atau memposting data ke titik akhir statistik:

function performanceObserver(list, observer) {
  list.getEntries().forEach(entry => {
  console.log('---');
    console.log(`name    : ${ entry.name }`);
    console.log(`type    : ${ entry.type }`);
    console.log(`start   : ${ entry.startTime }`);
    console.log(`duration: ${ entry.duration }`);
  });
}

Fungsi tersebut memiliki parameter berikut:

Lewati fungsi ini saat membuat objek PerformanceObserver baru lalu jalankan metodeobserve() dengan entryTypes untuk mengamati:

Menambahkan tanda atau ukuran baru sekarang akan menjalankan fungsi performanceObserver() dan menampilkan detail tentang pengukuran tersebut.

Opsi kinerja masa depan

Browser berbasis Chrome menawarkan performance.memoryproperty non-standar yang mengembalikan satu objek MemoryInfo:

Di mana:

  • jsHeapSizeLimit — ukuran maksimum heap dalam byte
  • totalJSHeapSize — total ukuran heap yang dialokasikan dalam byte, dan
  • usedJSHeapSize — Segmen tumpukan JS yang sedang aktif dalam byte.

Frame timing API tidak diterapkan di browser apa pun, tetapi akan mencatat jumlah kerja browser dalam satu iterasi loop peristiwa. Hal ini mencakup pemrosesan peristiwa DOM, animasi CSS, rendering, pengguliran, pengubahan ukuran, dll. API harus dapat melaporkan potensi gangguan ketika sebuah frame memerlukan waktu lebih dari 16,7 milidetik sehingga pembaruan turun di bawah 60 frame per detik.

Terakhir, “API Profil Mandiri” adalah “fitur eksperimental yang sedang dikembangkan di Chrome”. Mengingat tingkat sampel, API akan membantu menemukan kode yang lambat atau tidak diperlukan dengan cara yang mirip dengan laporan kinerja DevTools:

// define a new profiler with 10ms sample rate
const profile = await performance.profile({ sampleInterval: 10 });
// run code
doSomething();
// stop the profiler and capture a trace
const trace = await profile.stop();

Temukan masalah kinerja

Sangat mudah untuk berasumsi bahwa aplikasi Anda berjalan dengan baik ketika Anda mengembangkannya di PC baru yang terhubung ke jaringan cepat. Performance API menawarkan cara untuk membuktikan — atau menyangkal — masalah performa dengan mengumpulkan metrik pengguna sebenarnya berdasarkan perangkat, koneksi, dan lokasi mereka.

Awalnya diterbitkan di https://blog.asayer.io pada 12 Mei 2021.