Una introducción a la API de rendimiento
Publicado: 2022-08-10La API de rendimiento mide la capacidad de respuesta de su aplicación web en vivo en dispositivos de usuarios reales y conexiones de red. Puede ayudar a identificar cuellos de botella en el código del lado del cliente y del lado del servidor con:
- sincronización del usuario: medición personalizada del rendimiento de la función de JavaScript del lado del cliente
- tiempo de pintura: métricas de representación del navegador
- sincronización de recursos: rendimiento de carga de activos y llamadas Ajax
- tiempo de navegación: métricas de carga de página, incluidos redireccionamientos, búsquedas de DNS, preparación de DOM y más
La API aborda varios problemas asociados con la evaluación de desempeño típica:
- Los desarrolladores a menudo prueban aplicaciones en PC de gama alta conectadas a una red rápida. DevTools puede emular dispositivos más lentos, pero no siempre resaltará los problemas del mundo real cuando la mayoría de los clientes utilizan un móvil de dos años conectado al WiFi del aeropuerto.
- Las opciones de terceros, como Google Analytics, a menudo se bloquean, lo que genera resultados y suposiciones sesgados. También puede encontrar implicaciones de privacidad en algunos países.
- La API de rendimiento puede medir con precisión varias métricas mejor que métodos como
Date()
.
Las siguientes secciones describen las formas en que puede usar la API de rendimiento. Se recomienda cierto conocimiento de JavaScript y métricas de carga de páginas.
Disponibilidad de la API de rendimiento
La mayoría de los navegadores modernos admiten la API de rendimiento, incluidos IE10 e IE11 (incluso IE9 tiene soporte limitado). Puede detectar la presencia de la API usando:
if ('performance' in window) { // use Performance API }
No es posible Polyfill por completo la API, así que tenga cuidado con los navegadores que faltan. Si el 90 % de sus usuarios navegan felizmente con Internet Explorer 8, solo estaría midiendo el 10 % de los clientes con aplicaciones más capaces.
La API se puede usar en Web Workers, que proporciona una forma de ejecutar cálculos complejos en un subproceso en segundo plano sin detener las operaciones del navegador.
La mayoría de los métodos API se pueden usar en Node.js del lado del servidor con el módulo perf_hooks estándar:
// Node.js performance import { performance } from 'node:perf_hooks'; // or in Common JS: const { performance } = require('node:perf_hooks'); console.log( performance.now() );
Deno proporciona la API de rendimiento estándar:
// Deno performance console.log( performance.now() );
Deberá ejecutar secuencias de comandos con el permiso --allow-hrtime
para habilitar la medición de tiempo de alta resolución:
deno run --allow-hrtime index.js
El rendimiento del lado del servidor suele ser más fácil de evaluar y administrar porque depende de la carga, las CPU, la RAM, los discos duros y los límites del servicio en la nube. Las actualizaciones de hardware o las opciones de gestión de procesos, como PM2, agrupación en clústeres y Kubernetes, pueden ser más eficaces que la refactorización del código.
Las siguientes secciones se concentran en el rendimiento del lado del cliente por este motivo.
Medición de rendimiento personalizado
La API de rendimiento se puede utilizar para cronometrar la velocidad de ejecución de las funciones de su aplicación. Es posible que haya usado o encontrado funciones de tiempo usando Date()
:
const timeStart = new Date(); runMyCode(); const timeTaken = new Date() - timeStart; console.log(`runMyCode() executed in ${ timeTaken }ms`);
La API de rendimiento ofrece dos beneficios principales:
- Mejor precisión:
Date()
mide al milisegundo más cercano, pero la API de rendimiento puede medir fracciones de milisegundo (según el navegador). - Mejor confiabilidad: el usuario o el sistema operativo pueden cambiar la hora del sistema, por lo que las métricas basadas en
Date()
no siempre serán precisas. ¡Esto significa que sus funciones podrían parecer particularmente lentas cuando los relojes avanzan!
El equivalente de Date()
es performance.now()
que devuelve una marca de tiempo de alta resolución que se establece en cero cuando comienza el proceso responsable de crear el documento (la página se ha cargado):
const timeStart = performance.now(); runMyCode(); const timeTaken = performance.now() - timeStart; console.log(`runMyCode() executed in ${ timeTaken }ms`);
Una propiedad performance.timeOrigin
no estándar también puede devolver una marca de tiempo del 1 de enero de 1970, aunque esto no está disponible en IE y Deno.
performance.now()
se vuelve poco práctico cuando se realizan más de unas pocas mediciones. La API de rendimiento proporciona un búfer donde puede registrar eventos para su posterior análisis pasando un nombre de etiqueta a 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');
Se puede extraer una matriz de todos los objetos de marca en el búfer de rendimiento usando:
const mark = performance.getEntriesByType('mark');
Resultado de ejemplo:
[ { 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 }, ... ]
El método performance.measure()
calcula el tiempo entre dos marcas y también lo almacena en el búfer de rendimiento. Pasa un nuevo nombre de medida, el nombre de la marca inicial (o nulo para medir desde la carga de la página) y el nombre de la marca final (o nulo para medir el tiempo actual):
performance.measure('init', 'start:init', 'end:init');
Se agrega un objeto PerformanceMeasure al búfer con la duración de tiempo calculada. Para obtener este valor, puede solicitar una matriz de todas las medidas:
const measure = performance.getEntriesByType('measure');
o solicitar una medida por su nombre:
performance.getEntriesByName('init');
Resultado de ejemplo:
[ { detail: null duration: 99 entryType: "measure" name: "init" startTime: 1001 } ]
Uso del búfer de rendimiento
Además de las marcas y medidas, el búfer de rendimiento se utiliza para registrar automáticamente el tiempo de navegación, el tiempo de recursos y el tiempo de pintura (que veremos más adelante). Puede obtener una matriz de todas las entradas en el búfer:
performance.getEntries();
De forma predeterminada, la mayoría de los navegadores proporcionan un búfer que almacena hasta 150 métricas de recursos. Esto debería ser suficiente para la mayoría de las evaluaciones, pero puede aumentar o disminuir el límite del búfer si es necesario:
// record 500 metrics performance.setResourceTimingBufferSize(500);
Las marcas se pueden borrar por nombre o puede especificar un valor vacío para borrar todas las marcas:
performance.clearMarks('start:init');
Del mismo modo, las medidas se pueden borrar por nombre o un valor vacío para borrar todo:
performance.clearMeasures();
Supervisión de actualizaciones de búfer de rendimiento
Un PerformanceObserver puede monitorear los cambios en el búfer de rendimiento y ejecutar una función cuando ocurren eventos específicos. La sintaxis le resultará familiar si ha utilizado MutationObserver para responder a las actualizaciones de DOM o IntersectionObserver para detectar cuándo se desplazan los elementos en la ventana gráfica.
Debe definir una función de observador con dos parámetros:
- una serie de entradas de observadores que han sido detectadas, y
- el objeto observador. Si es necesario, se puede llamar a su método de
disconnect()
para detener al observador.
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 }`); }); }
La función se pasa a un nuevo objeto PerformanceObserver. A su método observe()
se le pasa una matriz de tipos de entrada del búfer de rendimiento para observar:
let observer = new PerformanceObserver( performanceCallback ); observer.observe({ entryTypes: ['mark', 'measure'] });
En este ejemplo, agregar una nueva marca o medida ejecuta la performanceCallback()
. Si bien solo registra mensajes aquí, podría usarse para activar una carga de datos o realizar cálculos adicionales.

Medición del rendimiento de la pintura
Paint Timing API solo está disponible en JavaScript del lado del cliente y registra automáticamente dos métricas que son importantes para Core Web Vitals:
- first-paint: el navegador ha comenzado a dibujar la página.
- first-contentful-paint: el navegador ha pintado el primer elemento significativo del contenido DOM, como un encabezado o una imagen.
Estos se pueden extraer del búfer de rendimiento a una matriz:
const paintTimes = performance.getEntriesByType('paint');
Tenga cuidado al ejecutar esto antes de que la página se haya cargado por completo; los valores no estarán listos. Espere el evento window.load
o use un PerformanceObserver
para monitorear los tipos de entrada de paint
.
Resultado de ejemplo:
[ { "name": "first-paint", "entryType": "paint", "startTime": 812, "duration": 0 }, { "name": "first-contentful-paint", "entryType": "paint", "startTime": 856, "duration": 0 } ]
Una primera pintura lenta a menudo es causada por CSS o JavaScript que bloquean el renderizado. La brecha con la primera pintura con contenido podría ser grande si el navegador tiene que descargar una imagen grande o representar elementos complejos.
Medición del rendimiento de los recursos
Los tiempos de red para recursos como imágenes, hojas de estilo y archivos JavaScript se registran automáticamente en el búfer de rendimiento. Si bien es poco lo que puede hacer para resolver los problemas de velocidad de la red (aparte de reducir el tamaño de los archivos), puede ayudar a resaltar problemas con activos más grandes, respuestas lentas de Ajax o scripts de terceros de bajo rendimiento.
Se puede extraer una matriz de métricas de PerformanceResourceTiming del búfer usando:
const resources = performance.getEntriesByType('resource');
Alternativamente, puede obtener métricas para un activo pasando su URL completa:
const resource = performance.getEntriesByName('https://test.com/script.js');
Resultado de ejemplo:
[ { 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 } ]
Se pueden examinar las siguientes propiedades:
- nombre : URL del recurso
- tipo de entrada : "recurso"
- initiatorType : cómo se inició el recurso, como "guión" o "enlace"
- serverTiming : una matriz de objetos
PerformanceServerTiming
pasados por el servidor en el encabezado HTTP Server-Timing (su aplicación del lado del servidor podría enviar métricas al cliente para un análisis más detallado) - startTime : marca de tiempo cuando comenzó la búsqueda
- nextHopProtocol : Protocolo de red utilizado
- workerStart : marca de tiempo antes de iniciar un trabajador de servicio de aplicación web progresiva (0 si la solicitud no es interceptada por un trabajador de servicio)
- redirectStart : marca de tiempo cuando comenzó una redirección
- redirectEnd : marca de tiempo después del último byte de la última respuesta de redirección
- fetchStart : marca de tiempo antes de la obtención del recurso
- domainLookupStart : marca de tiempo antes de una búsqueda de DNS
- domainLookupEnd : marca de tiempo después de la búsqueda de DNS
- connectStart : marca de tiempo antes de establecer una conexión con el servidor
- connectEnd : marca de tiempo después de establecer una conexión con el servidor
- SecureConnectionStart : marca de tiempo antes del protocolo de enlace SSL
- requestStart : marca de tiempo antes de que el navegador solicite el recurso
- responseStart : marca de tiempo cuando el navegador recibe el primer byte de datos
- responseEnd : marca de tiempo después de recibir el último byte o cerrar la conexión
- duración : La diferencia entre startTime y responseEnd
- transferSize : el tamaño del recurso en bytes, incluido el encabezado y el cuerpo comprimido
- encodedBodySize : el cuerpo del recurso en bytes antes de descomprimir
- decodedBodySize : el cuerpo del recurso en bytes después de descomprimir
Este script de ejemplo recupera todas las solicitudes de Ajax iniciadas por la API Fetch y devuelve el tamaño y la duración total de la transferencia:
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 } );
Medición del rendimiento de la navegación
Los tiempos de red para descargar la página anterior y cargar la página actual se registran automáticamente en el búfer de rendimiento como un único objeto PerformanceNavigationTiming
.
Extraerlo a una matriz usando:
const pageTime = performance.getEntriesByType('navigation');
…o pasando la URL de la página a .getEntriesByName()
:
const pageTiming = performance.getEntriesByName(window.location);
Las métricas son idénticas a las de los recursos, pero también incluyen valores específicos de la página:
- tipo de entrada : por ejemplo, "navegación"
- tipo : ya sea "navegar", "recargar", "retroceder_adelante" o "preprocesar"
- redirectCount : el número de redireccionamientos
- unloadEventStart : marca de tiempo antes del evento de descarga del documento anterior
- unloadEventEnd : marca de tiempo después del evento de descarga del documento anterior
- domInteractive : marca de tiempo cuando el navegador analizó el HTML y construyó el DOM
- domContentLoadedEventStart : marca de tiempo antes de que se active el evento DOMContentLoaded del documento
- domContentLoadedEventEnd : marca de tiempo después de que se complete el evento DOMContentLoaded del documento
- domComplete : marca de tiempo después de que se hayan completado la construcción de DOM y los eventos DOMContentLoaded
- loadEventStart : marca de tiempo antes de que se active el evento de carga de la página
- loadEventEnd : marca de tiempo después del evento de carga de la página y todos los activos están disponibles
Los problemas típicos incluyen:
- Un largo retraso entre unloadEventEnd y domInteractive . Esto podría indicar una respuesta lenta del servidor.
- Un largo retraso entre domContentLoadedEventStart y domComplete . Esto podría indicar que los scripts de inicio de página son demasiado lentos.
- Un largo retraso entre domComplete y loadEventEnd . Esto podría indicar que la página tiene demasiados recursos o que varios tardan demasiado en cargarse.
Registro y análisis de rendimiento
La API de rendimiento le permite recopilar datos de uso del mundo real y cargarlos en un servidor para su posterior análisis. Puede usar un servicio de terceros como Google Analytics para almacenar los datos, pero existe el riesgo de que el script de terceros se bloquee o presente nuevos problemas de rendimiento. Su propia solución se puede personalizar según sus requisitos para garantizar que el monitoreo no afecte otras funciones.
Tenga cuidado con las situaciones en las que no se pueden determinar las estadísticas, tal vez porque los usuarios utilizan navegadores antiguos, bloquean JavaScript o están detrás de un proxy corporativo. Comprender qué datos faltan puede ser más fructífero que hacer suposiciones basadas en información incompleta.
Idealmente, sus secuencias de comandos de análisis no afectarán negativamente el rendimiento al ejecutar cálculos complejos o cargar grandes cantidades de datos. Considere utilizar trabajadores web y minimizar el uso de llamadas localStorage síncronas. Siempre es posible procesar por lotes los datos sin procesar más adelante.
Por último, tenga cuidado con los valores atípicos, como dispositivos y conexiones muy rápidos o muy lentos, que afectan negativamente a las estadísticas. Por ejemplo, si nueve usuarios cargan una página en dos segundos pero el décimo experimenta una descarga de 60 segundos, la latencia promedio es de casi 8 segundos. Una métrica más realista es la cifra mediana (2 segundos) o el percentil 90 (9 de cada 10 usuarios experimentan un tiempo de carga de 2 segundos o menos).
Resumen
El rendimiento web sigue siendo un factor crítico para los desarrolladores. Los usuarios esperan que los sitios y las aplicaciones respondan en la mayoría de los dispositivos. La optimización de motores de búsqueda también puede verse afectada a medida que los sitios más lentos se degradan en Google.
Existen muchas herramientas de monitoreo de rendimiento, pero la mayoría evalúa las velocidades de ejecución del lado del servidor o usa un número limitado de clientes capaces para juzgar la representación del navegador. La API de rendimiento proporciona una forma de recopilar métricas de usuarios reales que no sería posible calcular de otra manera.