Descubre cómo encontrar interacciones lentas en los datos de campo de tu sitio web para que puedas encontrar oportunidades para mejorar su Interaction to Next Paint.
Los datos de campo son datos que te indican cómo los usuarios reales experimentan tu sitio web. Revela problemas que no puedes encontrar solo en los datos de laboratorio. En lo que respecta a la Interaction to Next Paint (INP), los datos de campo son fundamentales para identificar las interacciones lentas y proporcionan pistas vitales para ayudarte a corregirlas.
En esta guía, aprenderás a evaluar rápidamente el INP de tu sitio web con los datos de campo del Informe sobre la experiencia del usuario en Chrome (CrUX) para ver si tu sitio web tiene problemas con el INP. Luego, aprenderás a usar la compilación de atribución de la biblioteca de JavaScript de web-vitals y las nuevas estadísticas que proporciona la API de Long Animation Frames (LoAF) para recopilar e interpretar datos de campo sobre las interacciones lentas en tu sitio web.
Comienza con CrUX para evaluar el INP de tu sitio web
Si no recopilas datos de campo de los usuarios de tu sitio web, CrUX puede ser un buen punto de partida. CrUX recopila datos de campo de usuarios reales de Chrome que aceptaron enviar datos de telemetría.
Los datos de CrUX se muestran en varias áreas diferentes, y esto depende del alcance de la información que buscas. CrUX puede proporcionar datos sobre el INP y otras Métricas web esenciales para lo siguiente:
- Páginas individuales y orígenes completos con PageSpeed Insights
- Tipos de páginas Por ejemplo, muchos sitios web de comercio electrónico tienen tipos de páginas de detalles del producto y de fichas de productos. Puedes obtener datos de CrUX para tipos de páginas únicos en Search Console.
Como punto de partida, puedes ingresar la URL de tu sitio web en PageSpeed Insights. Una vez que ingreses la URL, se mostrarán los datos de campo correspondientes (si están disponibles) para varias métricas, incluido el INP. También puedes usar los botones de activación para verificar tus valores del INP en las dimensiones de dispositivos móviles y computadoras de escritorio.

Estos datos son útiles porque te indican si tienes un problema. Sin embargo, CrUX no puede decirte qué está causando los problemas. Existen muchas soluciones de supervisión de usuarios reales (RUM) disponibles que te ayudarán a recopilar tus propios datos de campo de los usuarios de tu sitio web para responder esa pregunta. Una opción es recopilar esos datos de campo por tu cuenta con la biblioteca de JavaScript de web-vitals.
Recopila datos de campos con la biblioteca de JavaScript web-vitals
La biblioteca de JavaScript de web-vitals
es una secuencia de comandos que puedes cargar en tu sitio web para recopilar datos de los campos de los usuarios de tu sitio web. Puedes usarlo para registrar varias métricas, incluido el INP en los navegadores que lo admiten.
La compilación estándar de la biblioteca de web-vitals se puede usar para obtener datos básicos del INP de los usuarios en el campo:
import {onINP} from 'web-vitals';
onINP(({name, value, rating}) => {
console.log(name); // 'INP'
console.log(value); // 512
console.log(rating); // 'poor'
});
Para analizar los datos de campo de tus usuarios, deberás enviarlos a algún lugar:
import {onINP} from 'web-vitals';
onINP(({name, value, rating}) => {
// Prepare JSON to be sent for collection. Note that
// you can add anything else you'd want to collect here:
const body = JSON.stringify({name, value, rating});
// Use `sendBeacon` to send data to an analytics endpoint.
// For Google Analytics, see https://github.com/GoogleChrome/web-vitals#send-the-results-to-google-analytics.
navigator.sendBeacon('/analytics', body);
});
Sin embargo, estos datos por sí solos no te brindan mucha más información que CrUX. Aquí es donde entra en juego la compilación de atribución de la biblioteca de web-vitals.
Llega más lejos con la compilación de atribución de la biblioteca de web-vitals
La compilación de atribución de la biblioteca de métricas web expone datos adicionales que puedes obtener de los usuarios en el campo para ayudarte a solucionar mejor las interacciones problemáticas que afectan el INP de tu sitio web. Se puede acceder a estos datos a través del objeto attribution
que se muestra en el método onINP()
de la biblioteca:
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, rating, attribution}) => {
console.log(name); // 'INP'
console.log(value); // 56
console.log(rating); // 'good'
console.log(attribution); // Attribution data object
});

Además del INP de la página, la compilación de atribución proporciona muchos datos que puedes usar para comprender los motivos de las interacciones lentas, incluida la parte de la interacción en la que debes enfocarte. Puede ayudarte a responder preguntas importantes, como las siguientes:
- "¿El usuario interactuó con la página mientras se cargaba?"
- "¿Los controladores de eventos de la interacción se ejecutaron durante mucho tiempo?"
- "¿Se retrasó el inicio del código del controlador de eventos de interacción? Si es así, ¿qué más sucedía en el subproceso principal en ese momento?"
- "¿La interacción causó mucho trabajo de renderización que retrasó la pintura del siguiente fotograma?"
En la siguiente tabla, se muestran algunos de los datos de atribución básicos que puedes obtener de la biblioteca y que pueden ayudarte a determinar algunas causas generales de las interacciones lentas en tu sitio web:
Clave del objeto attribution
|
Datos |
---|---|
interactionTarget
|
Es un selector CSS que apunta al elemento que produjo el valor del INP de la página, por ejemplo, button#save .
|
interactionType
|
Es el tipo de interacción, ya sea a partir de clics, presiones o entradas de teclado. |
inputDelay *
|
Es el retraso de entrada de la interacción. |
processingDuration *
|
Es el tiempo transcurrido desde que el primer objeto de escucha de eventos comenzó a ejecutarse en respuesta a la interacción del usuario hasta que finalizó el procesamiento de todos los objetos de escucha de eventos. |
presentationDelay *
|
El retraso de presentación de la interacción, que se produce desde que finalizan los controladores de eventos hasta el momento en que se pinta el siguiente fotograma. |
longAnimationFrameEntries *
|
Son las entradas del LoAF asociadas a la interacción. Consulta la siguiente sección para obtener más información. |
A partir de la versión 4 de la biblioteca web-vitals, puedes obtener estadísticas aún más detalladas sobre las interacciones problemáticas a través de los datos que proporciona con los desgloses de fases del INP (demora de entrada, duración del procesamiento y demora de presentación) y la API de Long Animation Frames (LoAF).
La API de Long Animation Frames (LoAF)
Depurar interacciones con datos de campo es una tarea difícil. Sin embargo, con los datos de LoAF, ahora es posible obtener mejores estadísticas sobre las causas de las interacciones lentas, ya que LoAF expone varios tiempos detallados y otros datos que puedes usar para identificar las causas precisas y, lo que es más importante, dónde se encuentra la fuente del problema en el código de tu sitio web.
La compilación de atribución de la biblioteca de web-vitals expone un array de entradas de LoAF en la clave longAnimationFrameEntries
del objeto attribution
. En la siguiente tabla, se enumeran algunos datos clave que puedes encontrar en cada entrada de la LoAF:
Clave del objeto de entrada de LoAF | Datos |
---|---|
duration
|
Es la duración del fotograma de animación largo, hasta que finaliza el diseño, pero sin incluir la pintura ni la composición. |
blockingDuration
|
Es la cantidad total de tiempo en el fotograma en el que el navegador no pudo responder rápidamente debido a tareas largas. Este tiempo de bloqueo puede incluir tareas largas que ejecutan JavaScript, así como cualquier tarea de renderización larga posterior en el fotograma. |
firstUIEventTimestamp
|
Es la marca de tiempo del momento en que el evento se puso en cola durante el fotograma. Es útil para determinar el inicio de la demora de entrada de una interacción. |
startTime
|
Es la marca de tiempo de inicio del fotograma. |
renderStart
|
Indica cuándo comenzó el trabajo de renderización del fotograma. Esto incluye cualquier devolución de llamada de requestAnimationFrame (y devoluciones de llamada de ResizeObserver si corresponde), pero potencialmente antes de que comience cualquier trabajo de diseño o diseño.
|
styleAndLayoutStart
|
Se produce cuando se trabaja en el diseño o el diseño de un fotograma. Puede ser útil para determinar la duración del trabajo de diseño o diseño cuando se tienen en cuenta otras marcas de tiempo disponibles. |
scripts
|
Es un array de elementos que contiene información de atribución de la secuencia de comandos que contribuye al INP de la página. |

blockingDuration
).
Toda esta información puede decirte mucho sobre qué hace que una interacción sea lenta, pero el array scripts
que muestran las entradas de LoAF debería ser de especial interés:
Clave del objeto de atribución de la secuencia de comandos | Datos |
---|---|
invoker
|
Es el invocador. Esto puede variar según el tipo de invocador que se describe en la siguiente fila. Algunos ejemplos de invocadores pueden ser valores como 'IMG#id.onload' , 'Window.requestAnimationFrame' o 'Response.json.then' . |
invokerType
|
Es el tipo de invocador. Puede ser 'user-callback' , 'event-listener' , 'resolve-promise' , 'reject-promise' , 'classic-script' o 'module-script' .
|
sourceURL
|
Es la URL de la secuencia de comandos desde la que se originó el fotograma de animación largo. |
sourceCharPosition
|
Posición del carácter en el script identificado por sourceURL .
|
sourceFunctionName
|
Es el nombre de la función en la secuencia de comandos identificada. |
Cada entrada de este array contiene los datos que se muestran en esta tabla, lo que te brinda información sobre la secuencia de comandos que provocó la interacción lenta y cómo lo hizo.
Mide e identifica las causas comunes detrás de las interacciones lentas
Para que te hagas una idea de cómo podrías usar esta información, en esta guía, se explicará cómo puedes usar los datos de LoAF que se muestran en la biblioteca web-vitals
para determinar algunas causas de las interacciones lentas.
Duraciones de procesamiento largas
La duración del procesamiento de una interacción es el tiempo que tardan en ejecutarse hasta completarse las devoluciones de llamada del controlador de eventos registrado de la interacción y cualquier otra cosa que pueda suceder entre ellas. La biblioteca de web-vitals muestra las duraciones de procesamiento altas:
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, attribution}) => {
const {processingDuration} = attribution; // 512.5
});
Es natural pensar que la causa principal de una interacción lenta es que el código del controlador de eventos tardó demasiado en ejecutarse, pero no siempre es así. Una vez que confirmes que este es el problema, puedes profundizar con los datos de LoAF:
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, attribution}) => {
const {processingDuration} = attribution; // 512.5
// Get the longest script from LoAF covering `processingDuration`:
const loaf = attribution.longAnimationFrameEntries.at(-1);
const script = loaf?.scripts.toSorted((a, b) => b.duration - a.duration)[0];
if (script) {
// Get attribution for the long-running event handler:
const {invokerType} = script; // 'event-listener'
const {invoker} = script; // 'BUTTON#update.onclick'
const {sourceURL} = script; // 'https://example.com/app.js'
const {sourceCharPosition} = script; // 83
const {sourceFunctionName} = script; // 'update'
}
});
Como puedes ver en el fragmento de código anterior, puedes trabajar con los datos de LoAF para rastrear la causa precisa detrás de una interacción con valores de duración de procesamiento altos, incluidos los siguientes:
- El elemento y su objeto de escucha de eventos registrado.
- Archivo de secuencia de comandos y posición del carácter dentro de él que contiene el código del controlador de eventos de larga duración.
- Es el nombre de la función.
Este tipo de datos es invaluable. Ya no es necesario que te encargues de averiguar exactamente qué interacción o cuál de sus controladores de eventos fueron responsables de los valores de duración de procesamiento altos. Además, como las secuencias de comandos de terceros suelen registrar sus propios controladores de eventos, puedes determinar si tu código fue el responsable. En el caso del código que controlas, te recomendamos que investigues la optimización de tareas largas.
Retrasos de entrada prolongados
Si bien los controladores de eventos de larga duración son comunes, hay otras partes de la interacción que se deben tener en cuenta. Una parte ocurre antes de la duración del procesamiento, lo que se conoce como retraso de entrada. Es el tiempo que transcurre desde que el usuario inicia la interacción hasta el momento en que comienzan a ejecutarse las devoluciones de llamada del controlador de eventos, y sucede cuando el subproceso principal ya está procesando otra tarea. La compilación de atribución de la biblioteca de web-vitals puede indicarte la duración de la demora de entrada para una interacción:
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, attribution}) => {
const {inputDelay} = attribution; // 125.59439536
});
Si observas que algunas interacciones tienen demoras de entrada altas, deberás averiguar qué sucedía en la página en el momento de la interacción que causó la demora de entrada prolongada, y eso a menudo se reduce a si la interacción ocurrió mientras se cargaba la página o después.
¿Fue durante la carga de la página?
El subproceso principal suele estar más ocupado cuando se carga una página. Durante este tiempo, se ponen en cola y procesan todo tipo de tareas, y si el usuario intenta interactuar con la página mientras se realiza todo este trabajo, la interacción puede demorarse. Las páginas que cargan mucho código JavaScript pueden iniciar el trabajo para compilar y evaluar secuencias de comandos, así como ejecutar funciones que preparan una página para las interacciones del usuario. Este trabajo puede interferir si el usuario interactúa mientras se produce esta actividad, y puedes averiguar si ese es el caso de los usuarios de tu sitio web:
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, attribution}) => {
const {inputDelay} = attribution; // 125.59439536
// Get the longest script from the first LoAF entry:
const loaf = attribution.longAnimationFrameEntries[0];
const script = loaf?.scripts.toSorted((a, b) => b.duration - a.duration)[0];
if (script) {
// Invoker types can describe if script eval blocked the main thread:
const {invokerType} = script; // 'classic-script' | 'module-script'
const {sourceLocation} = script; // 'https://example.com/app.js'
}
});
Si registras estos datos en el campo y observas demoras de entrada altas y tipos de invocador de 'classic-script'
o 'module-script'
, es justo decir que los scripts de tu sitio tardan mucho en evaluarse y bloquean el subproceso principal el tiempo suficiente como para retrasar las interacciones. Puedes reducir este tiempo de bloqueo dividiendo tus secuencias de comandos en paquetes más pequeños, aplazando la carga del código que no se usa inicialmente para un momento posterior y auditando tu sitio en busca de código que no se use y que puedas quitar por completo.
¿Fue después de la carga de la página?
Si bien las demoras en la entrada suelen ocurrir mientras se carga una página, también es posible que ocurran después de que se cargue una página, debido a una causa completamente diferente. Las causas comunes de las demoras en la entrada después de la carga de la página pueden ser el código que se ejecuta periódicamente debido a una llamada setInterval
anterior o incluso las devoluciones de llamada de eventos que se pusieron en cola para ejecutarse antes y que aún se están procesando.
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, attribution}) => {
const {inputDelay} = attribution; // 125.59439536
// Get the longest script from the first LoAF entry:
const loaf = attribution.longAnimationFrameEntries[0];
const script = loaf?.scripts.toSorted((a, b) => b.duration - a.duration)[0];
if (script) {
const {invokerType} = script; // 'user-callback'
const {sourceURL} = script; // 'https://example.com/app.js'
const {sourceCharPosition} = script; // 83
const {sourceFunctionName} = script; // 'update'
}
});
Al igual que con la solución de problemas relacionados con valores altos de duración del procesamiento, las demoras de entrada altas debido a las causas mencionadas anteriormente te proporcionarán datos detallados de atribución de secuencias de comandos. Sin embargo, lo que sí cambia es el tipo de invocador, según la naturaleza del trabajo que retrasó la interacción:
'user-callback'
indica que la tarea de bloqueo provino desetInterval
,setTimeout
o inclusorequestAnimationFrame
.'event-listener'
indica que la tarea de bloqueo provino de una entrada anterior que se puso en cola y aún se está procesando.'resolve-promise'
y'reject-promise'
significan que la tarea de bloqueo provino de algún trabajo asíncrono que se inició antes y se resolvió o rechazó en el momento en que el usuario intentó interactuar con la página, lo que retrasó la interacción.
En cualquier caso, los datos de atribución de la secuencia de comandos te darán una idea de dónde comenzar a buscar y si la demora en la entrada se debió a tu propio código o al de una secuencia de comandos de terceros.
Retrasos prolongados en la presentación
Las demoras en la presentación son el último tramo de una interacción y comienzan cuando finalizan los controladores de eventos de la interacción, hasta el punto en el que se pintó el siguiente fotograma. Ocurren cuando el trabajo en un controlador de eventos debido a una interacción cambia el estado visual de la interfaz de usuario. Al igual que con las duraciones de procesamiento y los retrasos de entrada, la biblioteca de web-vitals puede indicarte cuánto duró el retraso de presentación de una interacción:
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, attribution}) => {
const {presentationDelay} = attribution; // 113.32307691
});
Si registras estos datos y observas demoras de presentación altas para las interacciones que contribuyen al INP de tu sitio web, los culpables pueden variar, pero aquí tienes algunas causas que debes tener en cuenta.
Trabajo costoso de diseño y disposición
Los retrasos prolongados en la presentación pueden deberse a un costoso recálculo de estilos y a un trabajo de diseño que surge por varias causas, incluidos los selectores CSS complejos y los tamaños grandes del DOM. Puedes medir la duración de este trabajo con los tiempos de LoAF que se muestran en la biblioteca de web-vitals:
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, attribution}) => {
const {presentationDelay} = attribution; // 113.32307691
// Get the longest script from the last LoAF entry:
const loaf = attribution.longAnimationFrameEntries.at(-1);
const script = loaf?.scripts.toSorted((a, b) => b.duration - a.duration)[0];
// Get necessary timings:
const {startTime} = loaf; // 2120.5
const {duration} = loaf; // 1002
// Figure out the ending timestamp of the frame (approximate):
const endTime = startTime + duration; // 3122.5
// Get the start timestamp of the frame's style/layout work:
const {styleAndLayoutStart} = loaf; // 3011.17692309
// Calculate the total style/layout duration:
const styleLayoutDuration = endTime - styleAndLayoutStart; // 111.32307691
if (script) {
// Get attribution for the event handler that triggered
// the long-running style and layout operation:
const {invokerType} = script; // 'event-listener'
const {invoker} = script; // 'BUTTON#update.onclick'
const {sourceURL} = script; // 'https://example.com/app.js'
const {sourceCharPosition} = script; // 83
const {sourceFunctionName} = script; // 'update'
}
});
LoAF no te indicará la duración del trabajo de diseño y diseño para un fotograma, pero sí te indicará cuándo comenzó. Con esta marca de tiempo de inicio, puedes usar otros datos de LoAF para calcular una duración precisa de ese trabajo. Para ello, determina la hora de finalización del fotograma y réstale la marca de tiempo de inicio del trabajo de diseño y diseño.
Devoluciones de llamada de requestAnimationFrame
de larga duración
Una posible causa de las demoras prolongadas en la presentación es el trabajo excesivo que se realiza en una devolución de llamada de requestAnimationFrame
. El contenido de esta devolución de llamada se ejecuta después de que los controladores de eventos terminan de ejecutarse, pero justo antes del nuevo cálculo de estilo y el trabajo de diseño.
Estas devoluciones de llamada pueden tardar un tiempo considerable en completarse si el trabajo que se realiza en ellas es complejo. Si sospechas que los valores altos de demora en la presentación se deben al trabajo que realizas con requestAnimationFrame
, puedes usar los datos de LoAF que proporciona la biblioteca de web-vitals para identificar estas situaciones:
onINP(({name, value, attribution}) => {
const {presentationDelay} = attribution; // 543.1999999880791
// Get the longest script from the last LoAF entry:
const loaf = attribution.longAnimationFrameEntries.at(-1);
const script = loaf?.scripts.toSorted((a, b) => b.duration - a.duration)[0];
// Get the render start time and when style and layout began:
const {renderStart} = loaf; // 2489
const {styleAndLayoutStart} = loaf; // 2989.5999999940395
// Calculate the `requestAnimationFrame` callback's duration:
const rafDuration = styleAndLayoutStart - renderStart; // 500.59999999403954
if (script) {
// Get attribution for the event handler that triggered
// the long-running requestAnimationFrame callback:
const {invokerType} = script; // 'user-callback'
const {invoker} = script; // 'FrameRequestCallback'
const {sourceURL} = script; // 'https://example.com/app.js'
const {sourceCharPosition} = script; // 83
const {sourceFunctionName} = script; // 'update'
}
});
Si ves que una parte significativa del tiempo de demora en la presentación se dedica a una devolución de llamada de requestAnimationFrame
, asegúrate de que el trabajo que realizas en estas devoluciones de llamada se limite a realizar el trabajo que genera una actualización real en la interfaz de usuario. Cualquier otro trabajo que no toque el DOM ni actualice los estilos retrasará innecesariamente la pintura del siguiente fotograma, así que ten cuidado.
Conclusión
Los datos de campo son la mejor fuente de información a la que puedes recurrir para comprender qué interacciones son problemáticas para los usuarios reales en el campo. Si confías en las herramientas de recopilación de datos de campo, como la biblioteca de JavaScript de web-vitals (o un proveedor de RUM), puedes tener más certeza sobre qué interacciones son más problemáticas y, luego, reproducir las interacciones problemáticas en el laboratorio y corregirlas.
Imagen hero de Unsplash, de Federico Respini.