O introducere în API-ul de performanță
Publicat: 2022-08-10Performance API măsoară capacitatea de răspuns a aplicației dvs. web live pe dispozitivele utilizatorilor reale și conexiunile de rețea. Poate ajuta la identificarea blocajelor în codul dvs. de la partea client și de pe partea serverului cu:
- sincronizarea utilizatorului: măsurare personalizată a performanței funcției JavaScript la nivelul clientului
- sincronizarea vopselei: valorile de randare ale browserului
- sincronizarea resurselor: performanța de încărcare a activelor și apelurile Ajax
- sincronizare de navigare: valori de încărcare a paginii, inclusiv redirecționări, căutări DNS, pregătirea DOM și multe altele
API-ul abordează mai multe probleme asociate cu evaluarea tipică a performanței:
- Dezvoltatorii testează adesea aplicațiile pe computere de vârf conectate la o rețea rapidă. DevTools poate emula dispozitive mai lente, dar nu va evidenția întotdeauna problemele din lumea reală atunci când majoritatea clienților rulează un telefon mobil vechi de doi ani, conectat la WiFi din aeroport.
- Opțiunile terțelor părți, cum ar fi Google Analytics, sunt adesea blocate, ceea ce duce la rezultate și presupuneri distorsionate. De asemenea, este posibil să întâmpinați implicații privind confidențialitatea în unele țări.
- API-ul de performanță poate evalua cu precizie diverse valori mai bine decât metode precum
Date()
.
Următoarele secțiuni descriu modalități în care puteți utiliza Performance API. Sunt recomandate anumite cunoștințe despre JavaScript și valorile de încărcare a paginii.
Disponibilitatea API de performanță
Majoritatea browserelor moderne acceptă Performance API – inclusiv IE10 și IE11 (chiar și IE9 are suport limitat). Puteți detecta prezența API-ului folosind:
if ('performance' in window) { // use Performance API }
Nu este posibil să completați complet API-ul, așa că aveți grijă la lipsa browserelor. Dacă 90% dintre utilizatorii dvs. navighează fericiți cu Internet Explorer 8, ați măsura doar 10% dintre clienții cu aplicații mai capabile.
API-ul poate fi utilizat în Web Workers, care oferă o modalitate de a executa calcule complexe într-un thread de fundal fără a opri operațiunile browserului.
Majoritatea metodelor API pot fi utilizate în Node.js de pe server cu modulul standard 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 oferă API-ul de performanță standard:
// Deno performance console.log( performance.now() );
Va trebui să rulați scripturi cu permisiunea --allow-hrtime
pentru a activa măsurarea timpului de înaltă rezoluție:
deno run --allow-hrtime index.js
Performanța la nivelul serverului este de obicei mai ușor de evaluat și gestionat, deoarece depinde de încărcare, procesoare, RAM, hard disk și limitele serviciului cloud. Actualizările hardware sau opțiunile de gestionare a proceselor, cum ar fi PM2, clustering și Kubernetes pot fi mai eficiente decât refactorizarea codului.
Următoarele secțiuni se concentrează pe performanța clientului din acest motiv.
Măsurarea performanței personalizată
Performance API poate fi folosit pentru a cronometra viteza de execuție a funcțiilor aplicației. Este posibil să fi folosit sau să fi întâlnit funcții de sincronizare folosind Date()
:
const timeStart = new Date(); runMyCode(); const timeTaken = new Date() - timeStart; console.log(`runMyCode() executed in ${ timeTaken }ms`);
Performance API oferă două avantaje principale:
- Precizie mai bună:
Date()
măsoară cu cea mai apropiată milisecundă, dar Performance API poate măsura fracțiuni de milisecundă (în funcție de browser). - Fiabilitate mai bună: utilizatorul sau sistemul de operare poate schimba ora sistemului, astfel încât valorile bazate pe
Date()
nu vor fi întotdeauna exacte. Aceasta înseamnă că funcțiile dvs. pot părea deosebit de lente atunci când ceasurile avansează!
Echivalentul Date()
este performance.now()
care returnează un timestamp de înaltă rezoluție care este setat la zero când începe procesul responsabil de crearea documentului (pagina s-a încărcat):
const timeStart = performance.now(); runMyCode(); const timeTaken = performance.now() - timeStart; console.log(`runMyCode() executed in ${ timeTaken }ms`);
O proprietate non-standard performance.timeOrigin
poate returna, de asemenea, un marcaj temporal de la 1 ianuarie 1970, deși aceasta nu este disponibilă în IE și Deno.
performance.now()
devine nepractic atunci când se efectuează mai mult de câteva măsurători. API-ul Performance oferă un buffer în care puteți înregistra un eveniment pentru o analiză ulterioară, pasând un nume de etichetă la 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');
O matrice a tuturor obiectelor mark din tamponul de performanță poate fi extrasă folosind:
const mark = performance.getEntriesByType('mark');
Exemplu de rezultat:
[ { 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()
calculează timpul dintre două note și îl stochează, de asemenea, în tamponul de performanță. Transmiteți un nou nume de măsură, numele semnului de început (sau nul pentru a măsura de la încărcarea paginii) și numele marcajului de sfârșit (sau nul pentru a măsura la ora curentă):
performance.measure('init', 'start:init', 'end:init');
Un obiect PerformanceMeasure este atașat la tampon cu durata de timp calculată. Pentru a obține această valoare, puteți fie să solicitați o serie de toate măsurile:
const measure = performance.getEntriesByType('measure');
sau solicitați o măsură după numele ei:
performance.getEntriesByName('init');
Exemplu de rezultat:
[ { detail: null duration: 99 entryType: "measure" name: "init" startTime: 1001 } ]
Utilizarea tamponului de performanță
Pe lângă semne și măsuri, tamponul de performanță este folosit pentru a înregistra automat timpul de navigare, sincronizarea resurselor și sincronizarea vopselei (despre care vom discuta mai târziu). Puteți obține o matrice cu toate intrările din buffer:
performance.getEntries();
În mod implicit, majoritatea browserelor oferă un buffer care stochează până la 150 de valori ale resurselor. Acest lucru ar trebui să fie suficient pentru majoritatea evaluărilor, dar puteți crește sau reduce limita tamponului dacă este necesar:
// record 500 metrics performance.setResourceTimingBufferSize(500);
Marcajele pot fi șterse după nume sau puteți specifica o valoare goală pentru a șterge toate marcajele:
performance.clearMarks('start:init');
În mod similar, măsurile pot fi șterse după nume sau o valoare goală pentru a șterge toate:
performance.clearMeasures();
Monitorizarea actualizărilor tamponului de performanță
Un PerformanceObserver poate monitoriza modificările aduse tamponului de performanță și poate rula o funcție atunci când apar anumite evenimente. Sintaxa va fi familiară dacă ați folosit MutationObserver pentru a răspunde la actualizările DOM sau IntersectionObserver pentru a detecta când elementele sunt derulate în fereastra de vizualizare.
Trebuie să definiți o funcție de observator cu doi parametri:
- o serie de intrări de observator care au fost detectate și
- obiectul observator. Dacă este necesar, metoda sa
disconnect()
poate fi apelată pentru a opri observatorul.
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 }`); }); }
Funcția este transmisă unui nou obiect PerformanceObserver. Metodei sale observe()
i se transmite o matrice de tipuri de intrare a tamponului de performanță pentru a le observa:
let observer = new PerformanceObserver( performanceCallback ); observer.observe({ entryTypes: ['mark', 'measure'] });
În acest exemplu, adăugarea unui nou marcaj sau măsură rulează funcția performanceCallback()
. Deși înregistrează doar mesajele aici, ar putea fi folosit pentru a declanșa o încărcare de date sau pentru a face calcule suplimentare.
Măsurarea performanței vopselei
API-ul Paint Timing este disponibil numai în JavaScript la nivelul clientului și înregistrează automat două valori care sunt importante pentru Core Web Vitals:
- first-paint: browserul a început să deseneze pagina.
- first-contentful-paint: browserul a pictat primul element semnificativ al conținutului DOM, cum ar fi un titlu sau o imagine.
Acestea pot fi extrase din tamponul de performanță într-o matrice:
const paintTimes = performance.getEntriesByType('paint');
Aveți grijă să rulați acest lucru înainte ca pagina să se încarce complet; valorile nu vor fi gata. Fie așteptați evenimentul window.load
, fie utilizați un PerformanceObserver
pentru a monitoriza tipurile de intrare de tip paint
.
Exemplu de rezultat:
[ { "name": "first-paint", "entryType": "paint", "startTime": 812, "duration": 0 }, { "name": "first-contentful-paint", "entryType": "paint", "startTime": 856, "duration": 0 } ]
O primă vopsire lentă este adesea cauzată de blocarea redării CSS sau JavaScript. Diferența față de prima vopsea plină de conținut ar putea fi mare dacă browserul trebuie să descarce o imagine mare sau să redea elemente complexe.
Măsurarea performanței resurselor
Timpurile de rețea pentru resurse precum imagini, foi de stil și fișiere JavaScript sunt înregistrate automat în memoria tampon de performanță. Deși nu puteți face nimic pentru a rezolva problemele legate de viteza rețelei (în afară de reducerea dimensiunilor fișierelor), aceasta poate ajuta la evidențierea problemelor cu active mai mari, răspunsuri lente Ajax sau scripturi terțe cu performanțe proaste.
O serie de valori PerformanceResourceTiming poate fi extrasă din buffer folosind:
const resources = performance.getEntriesByType('resource');
Ca alternativă, puteți obține valori pentru un material prin transmiterea adresei URL complete:
const resource = performance.getEntriesByName('https://test.com/script.js');
Exemplu de rezultat:
[ { 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 } ]
Următoarele proprietăți pot fi examinate:
- nume : URL a resursei
- entryType : „resurse”
- initiatorType : cum a fost inițiată resursa, cum ar fi „script” sau „link”
- serverTiming : o serie de obiecte
PerformanceServerTiming
transmise de server în antetul HTTP Server-Timing (aplicația dvs. de pe partea serverului ar putea trimite valori către client pentru analize ulterioare) - startTime : marca temporală când a început preluarea
- nextHopProtocol : Protocolul de rețea utilizat
- workerStart : marca temporală înainte de a porni un lucrător al serviciului de aplicații web progresive (0 dacă cererea nu este interceptată de un lucrător al serviciului)
- redirectStart : Marca temporală când a început o redirecționare
- redirectEnd : Timp după ultimul octet al ultimului răspuns de redirecționare
- fetchStart : Timp înainte de preluarea resursei
- domainLookupStart : marca temporală înainte de o căutare DNS
- domainLookupEnd : Timp după căutarea DNS
- connectStart : marca temporală înainte de a stabili o conexiune la server
- connectEnd : Marca temporală după stabilirea unei conexiuni la server
- secureConnectionStart : marcaj de timp înainte de strângere de mână SSL
- requestStart : marca temporală înainte ca browserul să solicite resursa
- responseStart : Marca temporală când browserul primește primul octet de date
- responseEnd : Marca temporală după primirea ultimului octet sau închiderea conexiunii
- duration : diferența dintre startTime și responseEnd
- transferSize : dimensiunea resursei în octeți, inclusiv antetul și corpul comprimat
- encodedBodySize : corpul resursei în octeți înainte de decomprimare
- decodedBodySize : corpul resursei în octeți după decomprimare
Acest exemplu de script preia toate solicitările Ajax inițiate de API-ul Fetch și returnează dimensiunea și durata totală a transferului:
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 } );
Măsurarea performanței navigației
Timpurile de rețea pentru descărcarea paginii anterioare și încărcarea paginii curente sunt înregistrate automat în tamponul de performanță ca un singur obiect PerformanceNavigationTiming
.
Extrageți-l într-o matrice folosind:
const pageTime = performance.getEntriesByType('navigation');
…sau trecând adresa URL a paginii către .getEntriesByName()
:
const pageTiming = performance.getEntriesByName(window.location);
Valorile sunt identice cu cele pentru resurse, dar includ și valori specifice paginii:
- entryType : De exemplu, „navigație”
- tip : fie „navigați”, „reîncărcați”, „înapoi_înainte” sau „preaplicați”
- redirectCount : numărul de redirecționări
- unloadEventStart : Timp înainte de evenimentul de descărcare al documentului anterior
- unloadEventEnd : Timp după evenimentul de descărcare al documentului anterior
- domInteractive : Marca temporală când browserul a analizat HTML-ul și a construit DOM-ul
- domContentLoadedEventStart : marcaj temporal înainte ca evenimentul DOMContentLoaded al documentului să se declanșeze
- domContentLoadedEventEnd : Marca temporală după finalizarea evenimentului DOMContentLoaded al documentului
- domComplete : marcaj temporal după construcția DOM și evenimentele DOMContentLoaded au fost finalizate
- loadEventStart : marca temporală înainte ca evenimentul de încărcare a paginii să se declanșeze
- loadEventEnd : marca temporală după evenimentul de încărcare a paginii și toate elementele sunt disponibile
Problemele tipice includ:
- O întârziere mare între unloadEventEnd și domInteractive . Acest lucru ar putea indica un răspuns lent al serverului.
- O întârziere mare între domContentLoadedEventStart și domComplete . Acest lucru ar putea indica faptul că scripturile de pornire a paginii sunt prea lente.
- O întârziere mare între domComplete și loadEventEnd . Acest lucru ar putea indica că pagina are prea multe elemente sau că câteva durează prea mult să se încarce.
Înregistrarea și analiza performanței
Performance API vă permite să colecați date de utilizare din lumea reală și să le încărcați pe un server pentru analize ulterioare. Puteți utiliza un serviciu terță parte, cum ar fi Google Analytics, pentru a stoca datele, dar există riscul ca scriptul terță parte să fie blocat sau să introducă noi probleme de performanță. Propria dvs. soluție poate fi personalizată în funcție de cerințele dvs. pentru a vă asigura că monitorizarea nu afectează alte funcționalități.
Fiți atenți la situațiile în care statisticile nu pot fi determinate - poate pentru că utilizatorii se află pe browsere vechi, blochează JavaScript sau în spatele unui proxy corporativ. Înțelegerea datelor care lipsesc poate fi mai fructuoasă decât a face ipoteze bazate pe informații incomplete.
În mod ideal, scripturile dvs. de analiză nu vor avea un impact negativ asupra performanței prin efectuarea de calcule complexe sau încărcarea unor cantități mari de date. Luați în considerare utilizarea lucrătorilor web și reducerea la minimum a utilizării apelurilor locale de stocare sincrone. Este întotdeauna posibil să procesați în serie datele brute mai târziu.
În cele din urmă, aveți grijă de valorile aberante, cum ar fi dispozitivele și conexiunile foarte rapide sau foarte lente, care afectează negativ statisticile. De exemplu, dacă nouă utilizatori încarcă o pagină în două secunde, dar al zecelea experimentează o descărcare de 60 de secunde, latența medie ajunge la aproape 8 secunde. O valoare mai realistă este cifra mediană (2 secunde) sau a 90-a percentila (9 din 10 utilizatori experimentează un timp de încărcare de 2 secunde sau mai puțin).
rezumat
Performanța web rămâne un factor critic pentru dezvoltatori. Utilizatorii se așteaptă ca site-urile și aplicațiile să fie receptive pe majoritatea dispozitivelor. Optimizarea pentru motoarele de căutare poate fi, de asemenea, afectată, deoarece site-urile mai lente sunt retrogradate în Google.
Există o mulțime de instrumente de monitorizare a performanței, dar majoritatea evaluează vitezele de execuție pe partea serverului sau folosesc un număr limitat de clienți capabili pentru a evalua redarea browserului. API-ul de performanță oferă o modalitate de a aduna valori reale ale utilizatorilor pe care nu le-ar putea calcula în alt mod.