Wprowadzenie do Performance API

Opublikowany: 2022-08-10

Performance API mierzy czas reakcji Twojej działającej aplikacji internetowej na rzeczywistych urządzeniach użytkowników i połączeniach sieciowych. Może pomóc w identyfikacji wąskich gardeł w kodzie po stronie klienta i serwera za pomocą:

  • czas użytkownika: niestandardowy pomiar wydajności funkcji JavaScript po stronie klienta
  • czas malowania: metryki renderowania przeglądarki
  • taktowanie zasobów: ładowanie wydajności zasobów i wywołań Ajax
  • czas nawigacji: wskaźniki ładowania strony, w tym przekierowania, wyszukiwania DNS, gotowość DOM i nie tylko

API rozwiązuje kilka problemów związanych z typową oceną wydajności:

  1. Deweloperzy często testują aplikacje na wysokiej klasy komputerach podłączonych do szybkiej sieci. DevTools może emulować wolniejsze urządzenia, ale nie zawsze podkreśla rzeczywiste problemy, gdy większość klientów korzysta z dwuletniego telefonu komórkowego podłączonego do WiFi na lotnisku.
  2. Opcje innych firm, takie jak Google Analytics, są często blokowane, co prowadzi do wypaczenia wyników i założeń. W niektórych krajach możesz również napotkać wpływ na prywatność.
  3. Interfejs API wydajności może dokładniej mierzyć różne metryki lepiej niż metody takie jak Date() .

Chcesz dowiedzieć się więcej o korzystaniu z Performance API? Zacznij tutaj... Kliknij, aby Tweet
W poniższych sekcjach opisano sposoby korzystania z interfejsu Performance API. Zalecana jest pewna znajomość języka JavaScript i wskaźników ładowania strony.

Dostępność interfejsu API wydajności

Większość nowoczesnych przeglądarek obsługuje Performance API – w tym IE10 i IE11 (nawet IE9 ma ograniczone wsparcie). Możesz wykryć obecność API za pomocą:

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

Nie jest możliwe pełne wypełnienie API, więc uważaj na brakujące przeglądarki. Jeśli 90% Twoich użytkowników korzysta z przeglądarki Internet Explorer 8, mierzysz tylko 10% klientów z bardziej wydajnymi aplikacjami.

Interfejs API może być używany w Web Workers, które umożliwiają wykonywanie złożonych obliczeń w wątku w tle bez zatrzymywania operacji przeglądarki.

Większość metod API można zastosować w Node.js po stronie serwera ze standardowym modułem perf_hooks:

 // Node.js performance import { performance } from 'node:perf_hooks'; // or in Common JS: const { performance } = require('node:perf_hooks'); console.log( performance.now() );

Deno zapewnia standardowy interfejs API wydajności:

 // Deno performance console.log( performance.now() );

Aby włączyć pomiar czasu w wysokiej rozdzielczości, musisz uruchomić skrypty z uprawnieniem --allow-hrtime :

 deno run --allow-hrtime index.js

Wydajność po stronie serwera jest zwykle łatwiejsza do oceny i zarządzania, ponieważ zależy od obciążenia, procesorów, pamięci RAM, dysków twardych i limitów usług w chmurze. Uaktualnienia sprzętu lub opcje zarządzania procesami, takie jak PM2, klastrowanie i Kubernetes, mogą być bardziej efektywne niż refaktoryzacja kodu.

Z tego powodu poniższe sekcje koncentrują się na wydajności po stronie klienta.

Niestandardowy pomiar wydajności

Interfejs API wydajności może służyć do określania czasu szybkości wykonywania funkcji aplikacji. Być może użyłeś lub napotkałeś funkcje pomiaru czasu za pomocą Date() :

 const timeStart = new Date(); runMyCode(); const timeTaken = new Date() - timeStart; console.log(`runMyCode() executed in ${ timeTaken }ms`);

Performance API oferuje dwie podstawowe korzyści:

  1. Lepsza dokładność: Date() mierzy z dokładnością do milisekundy, ale Performance API może mierzyć ułamki milisekundy (w zależności od przeglądarki).
  2. Większa niezawodność: użytkownik lub system operacyjny może zmienić czas systemowy, więc metryki oparte na Date() nie zawsze będą dokładne. Oznacza to, że Twoje funkcje mogą wydawać się szczególnie wolne, gdy zegary przesuwają się do przodu!

Odpowiednikiem Date() jest performance.now() , który zwraca znacznik czasu o wysokiej rozdzielczości, który jest ustawiony na zero, gdy rozpoczyna się proces odpowiedzialny za tworzenie dokumentu (strona została załadowana):

 const timeStart = performance.now(); runMyCode(); const timeTaken = performance.now() - timeStart; console.log(`runMyCode() executed in ${ timeTaken }ms`);

Niestandardowa właściwość performance.timeOrigin może również zwrócić znacznik czasu z 1 stycznia 1970, chociaż nie jest to dostępne w IE i Deno.

performance.now() staje się niepraktyczny przy wykonywaniu więcej niż kilku pomiarów. Performance API zapewnia bufor, w którym możesz rejestrować zdarzenia do późniejszej analizy, przekazując nazwę etykiety do performance.mark() :

 performance.mark('start:app'); performance.mark('start:init'); init(); // run initialization functions performance.mark('end:init'); performance.mark('start:funcX'); funcX(); // run another function performance.mark('end:funcX'); performance.mark('end:app');

Tablicę wszystkich obiektów mark w buforze wydajności można wyodrębnić za pomocą:

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

Przykładowy wynik:

 [ { detail: null duration: 0 entryType: "mark" name: "start:app" startTime: 1000 }, { detail: null duration: 0 entryType: "mark" name: "start:init" startTime: 1001 }, { detail: null duration: 0 entryType: "mark" name: "end:init" startTime: 1100 }, ... ]

Metoda performance.measure() oblicza czas między dwoma znacznikami i przechowuje go również w buforze Performance. Przekazujesz nową nazwę miary, nazwę początkowego znacznika (lub null, aby mierzyć od załadowania strony) i nazwę końcowego znacznika (lub null, aby mierzyć do bieżącego czasu):

 performance.measure('init', 'start:init', 'end:init');

Obiekt PerformanceMeasure jest dołączany do bufora z obliczonym czasem trwania. Aby uzyskać tę wartość, możesz zażądać tablicy wszystkich miar:

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

lub zażądać środka, podając jego nazwę:

 performance.getEntriesByName('init');

Przykładowy wynik:

 [ { detail: null duration: 99 entryType: "measure" name: "init" startTime: 1001 } ]

Korzystanie z bufora wydajności

Oprócz znaków i miar bufor wydajności jest używany do automatycznego rejestrowania czasu nawigacji, czasu zasobów i czasu malowania (które omówimy później). Możesz uzyskać tablicę wszystkich wpisów w buforze:

 performance.getEntries();

Domyślnie większość przeglądarek udostępnia bufor, który przechowuje do 150 metryk zasobów. To powinno wystarczyć w przypadku większości ocen, ale w razie potrzeby możesz zwiększyć lub zmniejszyć limit bufora:

 // record 500 metrics performance.setResourceTimingBufferSize(500);

Znaki można wyczyścić według nazwy lub możesz podać pustą wartość, aby usunąć wszystkie znaki:

 performance.clearMarks('start:init');

Podobnie miary można wyczyścić według nazwy lub pustej wartości, aby wyczyścić wszystkie:

 performance.clearMeasures();

Monitorowanie aktualizacji bufora wydajności

PerformanceObserver może monitorować zmiany w buforze wydajności i uruchamiać funkcję, gdy wystąpią określone zdarzenia. Składnia będzie znajoma, jeśli używałeś MutationObserver do reagowania na aktualizacje DOM lub IntersectionObserver do wykrywania, kiedy elementy są przewijane do rzutni.

Musisz zdefiniować funkcję obserwatora z dwoma parametrami:

  1. tablicę wpisów obserwatorów, które zostały wykryte, oraz
  2. obiekt obserwatora. W razie potrzeby można wywołać jego metodę disconnect() , aby zatrzymać obserwatora.
 function performanceCallback(list, observer) { list.getEntries().forEach(entry => { console.log(`name : ${ entry.name }`); console.log(`type : ${ entry.type }`); console.log(`start : ${ entry.startTime }`); console.log(`duration: ${ entry.duration }`); }); }

Funkcja jest przekazywana do nowego obiektu PerformanceObserver. Jej metoda Observe observe() jest przekazywana do obserwacji tablicy wpisów bufora wydajności:

 let observer = new PerformanceObserver( performanceCallback ); observer.observe({ entryTypes: ['mark', 'measure'] });

W tym przykładzie dodanie nowego znaku lub miary uruchamia funkcję performanceCallback() . Chociaż tutaj rejestruje tylko komunikaty, może być używany do wyzwalania przesyłania danych lub wykonywania dalszych obliczeń.

Pomiar wydajności farby

Paint Timing API jest dostępny tylko w języku JavaScript po stronie klienta i automatycznie rejestruje dwie metryki, które są ważne dla Core Web Vitals:

  1. pierwsze malowanie: przeglądarka zaczęła rysować stronę.
  2. first-contentful-paint: przeglądarka narysowała pierwszy znaczący element zawartości DOM, taki jak nagłówek lub obraz.

Można je wyodrębnić z bufora wydajności do tablicy:

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

Uważaj na uruchamianie tego, zanim strona zostanie w pełni załadowana; wartości nie będą gotowe. Poczekaj na zdarzenie window.load lub użyj PerformanceObserver do monitorowania wpisów paint .

Masz problemy z przestojami i WordPressem? Kinsta to rozwiązanie hostingowe zaprojektowane, aby zaoszczędzić Twój czas! Sprawdź nasze funkcje

Przykładowy wynik:

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

Powolne pierwsze malowanie jest często spowodowane blokowaniem renderowania przez CSS lub JavaScript. Luka do pierwszego malowania treści może być duża, jeśli przeglądarka musi pobrać duży obraz lub renderować złożone elementy.

Pomiar wydajności zasobów

Czasy sieciowe dla zasobów, takich jak obrazy, arkusze stylów i pliki JavaScript są automatycznie rejestrowane w buforze wydajności. Chociaż niewiele można zrobić, aby rozwiązać problemy z szybkością sieci (poza zmniejszeniem rozmiarów plików), może to pomóc w podkreśleniu problemów z większymi zasobami, powolnymi odpowiedziami Ajax lub źle działającymi skryptami innych firm.

Tablicę metryk PerformanceResourceTiming można wyodrębnić z bufora za pomocą:

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

Możesz też pobrać dane dotyczące zasobu, przekazując jego pełny adres URL:

 const resource = performance.getEntriesByName('https://test.com/script.js');

Przykładowy wynik:

 [ { connectEnd: 195, connectStart: 195, decodedBodySize: 0, domainLookupEnd: 195, domainLookupStart: 195, duration: 2, encodedBodySize: 0, entryType: "resource", fetchStart: 195, initiatorType: "script", name: "https://test.com/script.js", nextHopProtocol: "h3", redirectEnd: 0, redirectStart: 0, requestStart: 195, responseEnd: 197, responseStart: 197, secureConnectionStart: 195, serverTiming: [], startTime: 195, transferSize: 0, workerStart: 195 } ]

Można zbadać następujące właściwości:

  • nazwa : URL zasobu
  • entryType : „zasób”
  • initiatorType : sposób zainicjowania zasobu, na przykład „skrypt” lub „link”
  • serverTiming : tablica obiektów PerformanceServerTiming przekazywana przez serwer w nagłówku HTTP Server-Timing (Twoja aplikacja po stronie serwera może wysyłać metryki do klienta w celu dalszej analizy)
  • startTime : sygnatura czasowa rozpoczęcia pobierania
  • nextHopProtocol : używany protokół sieciowy
  • workerStart : sygnatura czasowa przed uruchomieniem progresywnego procesu roboczego usługi aplikacji sieci Web (0, jeśli żądanie nie jest przechwycone przez proces roboczy usługi)
  • redirectStart : sygnatura czasowa rozpoczęcia przekierowania
  • redirectEnd : Znacznik czasu po ostatnim bajcie ostatniej odpowiedzi na przekierowanie
  • fetchStart : znacznik czasu przed pobraniem zasobu
  • domainLookupStart : znacznik czasu przed wyszukiwaniem DNS
  • domainLookupEnd : znacznik czasu po wyszukiwaniu DNS
  • connectStart : Znacznik czasu przed nawiązaniem połączenia z serwerem
  • connectEnd : Znacznik czasu po nawiązaniu połączenia z serwerem
  • secureConnectionStart : znacznik czasu przed uzgadnianiem SSL
  • requestStart : znacznik czasu, zanim przeglądarka zażąda zasobu
  • responseStart : Znacznik czasu, kiedy przeglądarka otrzymuje pierwszy bajt danych
  • responseEnd : Znacznik czasu po odebraniu ostatniego bajtu lub zamknięciu połączenia
  • czas trwania : różnica między startTime i responseEnd
  • transferSize : rozmiar zasobu w bajtach, w tym nagłówek i skompresowana treść
  • encodedBodySize : treść zasobu w bajtach przed dekompresowaniem
  • decodedBodySize : treść zasobu w bajtach po rozpakowaniu

Ten przykładowy skrypt pobiera wszystkie żądania Ajax zainicjowane przez interfejs API Fetch i zwraca całkowity rozmiar i czas trwania transferu:

 const fetchAll = performance.getEntriesByType('resource') .filter( r => r.initiatorType === 'fetch') .reduce( (sum, current) => { return { transferSize: sum.transferSize += current.transferSize, duration: sum.duration += current.duration } }, { transferSize: 0, duration: 0 } );

Pomiar wydajności nawigacji

Chronometraż sieci dotyczący zwalniania poprzedniej strony i ładowania bieżącej strony jest automatycznie rejestrowany w buforze wydajności jako pojedynczy obiekt PerformanceNavigationTiming .

Wyodrębnij go do tablicy za pomocą:

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

…lub przekazując adres URL strony do .getEntriesByName() :

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

Dane są takie same jak w przypadku zasobów, ale obejmują również wartości specyficzne dla strony:

  • Typ wpisu : Np. „nawigacja”
  • wpisz : „nawiguj”, „przeładuj”, „wstecz_do przodu” lub „wstępnie wyrenderuj”
  • redirectCount : liczba przekierowań
  • unloadEventStart : znacznik czasu przed zdarzeniem wyładowania poprzedniego dokumentu
  • unloadEventEnd : Znacznik czasu po zdarzeniu wyładowania poprzedniego dokumentu
  • domInteractive : sygnatura czasowa, gdy przeglądarka przeanalizowała kod HTML i utworzyła DOM
  • domContentLoadedEventStart : sygnatura czasowa przed wywołaniem zdarzenia DOMContentLoaded dokumentu
  • domContentLoadedEventEnd : znacznik czasu po zakończeniu zdarzenia DOMContentLoaded dokumentu
  • domComplete : sygnatura czasowa po zakończeniu tworzenia DOM i zdarzeń DOMContentLoaded
  • loadEventStart : sygnatura czasowa przed uruchomieniem zdarzenia wczytywania strony
  • loadEventEnd : Znacznik czasu po zdarzeniu wczytania strony i wszystkie zasoby są dostępne

Typowe problemy to:

  • Duże opóźnienie między unloadEventEnd a domInteractive . Może to wskazywać na wolną odpowiedź serwera.
  • Duże opóźnienie między domContentLoadedEventStart a domComplete . Może to oznaczać, że skrypty uruchamiania strony są zbyt wolne.
  • Duże opóźnienie między domComplete a loadEventEnd . Może to oznaczać, że strona zawiera zbyt wiele zasobów lub wczytywanie kilku trwa zbyt długo.

Rejestracja i analiza wyników

Wydajny interfejs API umożliwia zestawianie rzeczywistych danych dotyczących użytkowania i przesyłanie ich na serwer w celu dalszej analizy. Do przechowywania danych możesz użyć usługi innej firmy, takiej jak Google Analytics, ale istnieje ryzyko zablokowania skryptu innej firmy lub wprowadzenia nowych problemów z wydajnością. Własne rozwiązanie można dostosować do własnych wymagań, aby zapewnić, że monitorowanie nie wpływa na inne funkcje.

Uważaj na sytuacje, w których nie można określić statystyk — być może dlatego, że użytkownicy korzystają ze starych przeglądarek, blokują JavaScript lub korzystają z firmowego serwera proxy. Zrozumienie, jakich danych brakuje, może być bardziej owocne niż przyjmowanie założeń na podstawie niekompletnych informacji.

Najlepiej byłoby, gdyby skrypty analityczne nie wpływały negatywnie na wydajność, wykonując złożone obliczenia lub przesyłając duże ilości danych. Rozważ wykorzystanie pracowników sieci Web i zminimalizowanie użycia synchronicznych wywołań localStorage. Zawsze istnieje możliwość późniejszego przetwarzania wsadowego nieprzetworzonych danych.

Na koniec uważaj na wartości odstające, takie jak bardzo szybkie lub bardzo wolne urządzenia i połączenia, które negatywnie wpływają na statystyki. Na przykład, jeśli dziewięciu użytkowników ładuje stronę w dwie sekundy, ale dziesiąty pobiera 60 sekund, średni czas oczekiwania wynosi prawie 8 sekund. Bardziej realistycznym wskaźnikiem jest mediana (2 sekundy) lub 90. percentyl (9 na 10 użytkowników ma czas ładowania wynoszący 2 sekundy lub mniej).

Streszczenie

Wydajność sieci pozostaje kluczowym czynnikiem dla programistów. Użytkownicy oczekują, że witryny i aplikacje będą responsywne na większości urządzeń. Optymalizacja pod kątem wyszukiwarek może również zostać zakłócona, ponieważ wolniejsze witryny są obniżane w Google.
Wszystko, co musisz wiedzieć, aby zacząć korzystać z Performance API, znajdziesz tutaj Kliknij, aby tweetować
Istnieje wiele narzędzi do monitorowania wydajności, ale większość ocenia szybkość wykonywania po stronie serwera lub używa ograniczonej liczby zdolnych klientów do oceny renderowania przeglądarki. Performance API zapewnia sposób na zestawienie rzeczywistych metryk użytkowników, których nie dałoby się obliczyć w żaden inny sposób.