Depura el rendimiento en el campo

Aprende a atribuir tus datos de rendimiento con información de depuración para ayudarte a identificar y solucionar problemas de usuarios reales con Analytics

Google ofrece dos categorías de herramientas para medir y depurar el rendimiento:

  • Herramientas del lab: Son herramientas como Lighthouse, que permite que la página se cargue en un entorno simulado que pueda imitar varias condiciones (por ejemplo, una red lenta y un dispositivo móvil de gama baja).
  • Herramientas de campo: Herramientas como el Informe sobre la experiencia del usuario en Chrome (CrUX), que se basa en datos agregados de usuarios reales de Chrome Ten en cuenta que los datos de campo que informan herramientas como PageSpeed Insights y Search Console provienen de datos de CrUX.

Si bien las herramientas de campo ofrecen datos más precisos (datos que realmente representan la experiencia real de los usuarios), las herramientas de lab suelen ser mejores para ayudarte a identificar y solucionar problemas.

Los datos de CrUX son más representativos del rendimiento real de tu página, pero es poco probable que conocer tus puntuaciones de CrUX te ayude a descubrir cómo mejorarlo.

Lighthouse, por otro lado, identificará los problemas y hará sugerencias específicas sobre cómo mejorar. Sin embargo, Lighthouse solo sugerirá problemas de rendimiento que detecte durante el tiempo de carga de la página. No detecta problemas que solo se manifiestan como resultado de la interacción del usuario, como el desplazamiento o el clic en los botones de la página.

Esto genera una pregunta importante: ¿cómo puedes capturar información de depuración de las Métricas web esenciales y otras métricas de rendimiento de usuarios reales en el campo?

En esta publicación, se explicará en detalle qué APIs puedes usar para recopilar información de depuración adicional para cada una de las métricas actuales de las Métricas web esenciales y te dará ideas para capturar estos datos en tu herramienta de estadísticas existente.

APIs para atribución y depuración

CLS

De todas las métricas web esenciales, el CLS es quizás la más importante para la que recopilar información de depuración en el campo. La métrica CLS se mide durante toda la vida útil de la página, por lo que la forma en que un usuario interactúa con ella (cuánto se desplaza, en qué hace clic, etc.) puede tener un impacto significativo en función de si hay cambios en el diseño y qué elementos cambian.

Considera el siguiente informe de PageSpeed Insights:

Un informe de PageSpeed Insights con diferentes valores de CLS

El valor informado para CLS del lab (Lighthouse) en comparación con el CLS del campo (datos de CrUX) es bastante diferente, y esto tiene sentido si consideras que la página puede tener mucho contenido interactivo que no se usa cuando se prueba en Lighthouse.

Sin embargo, incluso si comprendes que la interacción del usuario afecta los datos del campo, debes saber qué elementos de la página cambian para dar como resultado una puntuación de 0.3 en el percentil 75.

Esto es posible con la interfaz LayoutShiftAttribution.

Obtener la atribución de cambio de diseño

La interfaz LayoutShiftAttribution se expone en cada entrada layout-shift que emite la API de inestabilidad de diseño.

Para obtener una explicación detallada de estas dos interfaces, consulta Cómo depurar cambios de diseño. A los efectos de esta publicación, lo principal que debes saber es que, como desarrollador, puedes observar todos los cambios de diseño que ocurren en la página, así como los elementos que cambian.

Este es un código de ejemplo que registra cada cambio de diseño y los elementos que cambiaron:

new PerformanceObserver((list) => {
  for (const {value, startTime, sources} of list.getEntries()) {
    // Log the shift amount and other entry info.
    console.log('Layout shift:', {value, startTime});
    if (sources) {
      for (const {node, curRect, prevRect} of sources) {
        // Log the elements that shifted.
        console.log('  Shift source:', node, {curRect, prevRect});
      }
    }
  }
}).observe({type: 'layout-shift', buffered: true});

Es probable que no sea práctico medir y enviar datos a tu herramienta de estadísticas por cada cambio de diseño que ocurra. Sin embargo, si supervisas todos los cambios, podrás realizar un seguimiento de los peores cambios y solo informar la información sobre ellos.

El objetivo no es identificar y corregir cada cambio de diseño que ocurre para cada usuario, sino identificar los cambios que afectan a la mayor cantidad de usuarios y, por lo tanto, contribuyen más al CLS de tu página en el percentil 75.

Además, no necesitas calcular el elemento de origen más grande cada vez que haya un cambio, solo debes hacerlo cuando estés listo para enviar el valor de CLS a tu herramienta de estadísticas.

Con el siguiente código, se toma una lista de entradas layout-shift que contribuyeron a CLS y se muestra el elemento fuente más grande del cambio más grande:

function getCLSDebugTarget(entries) {
  const largestEntry = entries.reduce((a, b) => {
    return a && a.value > b.value ? a : b;
  });
  if (largestEntry && largestEntry.sources && largestEntry.sources.length) {
    const largestSource = largestEntry.sources.reduce((a, b) => {
      return a.node && a.previousRect.width * a.previousRect.height >
          b.previousRect.width * b.previousRect.height ? a : b;
    });
    if (largestSource) {
      return largestSource.node;
    }
  }
}

Una vez que hayas identificado el elemento más importante que contribuye al cambio más grande, puedes informarlo a tu herramienta de estadísticas.

Es probable que el elemento que más contribuye a CLS para una página determinada varíe de un usuario a otro, pero si agregas esos elementos de todos los usuarios, podrás generar una lista de elementos cambiantes que afectan a la mayor cantidad de usuarios.

Una vez que hayas identificado y corregido la causa raíz de los cambios para esos elementos, tu código de Analytics comenzará a informar cambios más pequeños como los "peores" cambios de tus páginas. En algún momento, todos los cambios informados serán lo suficientemente pequeños como para que tus páginas estén dentro del umbral “bueno” de 0.1.

Estos son otros metadatos que pueden ser útiles de capturar junto con el elemento de origen del cambio más grande:

  • La hora del cambio más importante
  • La ruta de URL en el momento del cambio más grande (para sitios que actualizan la URL de forma dinámica, como las aplicaciones de una sola página).

LCP

Para depurar el LCP en el campo, la información principal que necesitas es qué elemento en particular fue el más grande (el candidato de LCP) para esa carga de página en particular.

Ten en cuenta que es totalmente posible (de hecho, es bastante común) que el elemento candidato para LCP sea diferente de un usuario a otro, incluso para la misma página.

Esto puede suceder por varios motivos:

  • Los dispositivos de los usuarios tienen diferentes resoluciones de pantalla, lo que da como resultado diferentes diseños de página y, por lo tanto, distintos elementos visibles dentro del viewport.
  • Los usuarios no siempre cargan páginas desplazadas hasta el final. A menudo, los vínculos contienen identificadores de fragmentos o incluso fragmentos de texto, lo que significa que es posible que tus páginas se carguen y se muestren en cualquier posición de desplazamiento en la página.
  • Es posible que el contenido se personalice para el usuario actual, por lo que el elemento candidato para LCP puede variar considerablemente de un usuario a otro.

Esto significa que no puedes hacer suposiciones sobre qué elemento o conjunto de elementos será el elemento candidato para LCP más común de una página en particular. Debes medirla en función del comportamiento de los usuarios reales.

Identifica el elemento candidato para LCP

Para determinar el elemento candidato para LCP en JavaScript, puedes usar la API de Largest Contentful Paint, la misma API que usas para determinar el valor de tiempo de LCP.

Cuando observas entradas largest-contentful-paint, puedes determinar el elemento candidato para LCP actual si observas la propiedad element de la última entrada:

new PerformanceObserver((list) => {
  const entries = list.getEntries();
  const lastEntry = entries[entries.length - 1];

  console.log('LCP element:', lastEntry.element);
}).observe({type: 'largest-contentful-paint', buffered: true});

Una vez que conozcas el elemento candidato para LCP, puedes enviarlo a tu herramienta de estadísticas junto con el valor de la métrica. Al igual que con CLS, esto te ayudará a identificar qué elementos son más importantes para optimizar primero.

Además del elemento candidato para LCP, también puede ser útil medir los tiempos de las subpartes de LCP, que pueden ser útiles para determinar qué pasos de optimización específicos son relevantes para tu sitio.

FID

Para depurar el FID en el campo, es importante recordar que mide solo la parte del retraso de la latencia general del evento de primera entrada. Eso significa que lo que el usuario interactuó no es realmente tan importante como lo que estaba sucediendo en el subproceso principal en el momento en que interactuó.

Por ejemplo, muchas aplicaciones de JavaScript que admiten la renderización del servidor (SSR) proporcionarán HTML estático que se puede renderizar en la pantalla antes de que sea interactiva con la entrada del usuario, es decir, antes de que termine de cargarse el JavaScript necesario para hacer que el contenido sea interactivo.

Para estos tipos de aplicaciones, puede ser muy importante saber si la primera entrada ocurrió antes o después de la hidratación. Si resulta que muchas personas intentan interactuar con la página antes de que se complete la hidratación, considera renderizar tus páginas en un estado inhabilitado o de carga, en lugar de hacerlo en un estado que parezca interactivo.

Si el framework de tu aplicación expone la marca de tiempo de hidratación, puedes compararla con la marca de tiempo de la entrada first-input para determinar si la primera entrada ocurrió antes o después de la hidratación. Si tu framework no expone esa marca de tiempo o no usa la hidratación en absoluto, otro indicador útil puede ser si la entrada se produjo antes o después de que JavaScript terminara de cargarse.

El evento DOMContentLoaded se activa después de que el código HTML de la página se carga y analiza por completo, lo que incluye la espera de que se cargue cualquier secuencia de comandos síncrona, diferida o de módulo (incluidos todos los módulos importados de forma estática). Por lo tanto, puedes usar el tiempo de ese evento y compararlo con el momento en que se produjo el FID.

El siguiente código observa las entradas y los registros de first-input si la primera entrada ocurrió o no antes del final del evento DOMContentLoaded:

new PerformanceObserver((list) => {
  const fidEntry = list.getEntries()[0];
  const navEntry = performance.getEntriesByType('navigation')[0];
  const wasFIDBeforeDCL =
    fidEntry.startTime < navEntry.domContentLoadedEventStart;

  console.log('FID occurred before DOMContentLoaded:', wasFIDBeforeDCL);
}).observe({type: 'first-input', buffered: true});

Identifica el elemento de destino de FID y el tipo de evento

Los indicadores de depuración adicionales potencialmente útiles son el elemento con el que se interactuó y el tipo de interacción con el que fue (como mousedown, keydown y pointerdown). Si bien la interacción con el elemento en sí no contribuye al FID (recuerda que el FID es solo la parte de demora de la latencia total del evento), saber con qué elementos interactúan los usuarios puede ser útil para determinar la mejor manera de mejorar el FID.

Por ejemplo, si la gran mayoría de las primeras interacciones del usuario son con un elemento en particular, considera intercalar en el HTML el código JavaScript necesario para ese elemento y cargar de forma diferida el resto.

Para obtener el tipo de interacción y el elemento asociados con el primer evento de entrada, puedes hacer referencia a las propiedades target y name de la entrada first-input:

new PerformanceObserver((list) => {
  const fidEntry = list.getEntries()[0];

  console.log('FID target element:', fidEntry.target);
  console.log('FID interaction type:', fidEntry.name);
}).observe({type: 'first-input', buffered: true});

INP

INP es muy similar a FID, ya que los bits de información más útiles para capturar en el campo son los siguientes:

  1. Con qué elemento se interactuó
  2. ¿Por qué ocurrió el tipo de interacción?
  3. Cuándo tuvo lugar esa interacción

Al igual que los FID, una de las principales causas de las interacciones lentas es el bloqueo de un subproceso principal, lo que puede ser común mientras se carga JavaScript. Saber si las interacciones más lentas se producen durante la carga de la página es útil para determinar qué se debe hacer para solucionar el problema.

A diferencia de FID, la métrica de INP considera la latencia completa de una interacción, incluido el tiempo que lleva ejecutar los objetos de escucha de eventos registrados y el tiempo que tarda en dibujarse el siguiente fotograma después de que se ejecutaron todos los objetos de escucha de eventos. Esto significa que, para el INP, es aún más útil saber qué elementos de destino tienden a generar interacciones lentas y qué tipos de interacciones son.

Debido a que INP y FID se basan en la API de Event Timing, la forma en que determinas esta información en JavaScript es muy similar al ejemplo anterior. Con el siguiente código, se registra el elemento de destino y el tiempo (relativo a DOMContentLoaded) de la entrada de INP.

function logINPDebugInfo(inpEntry) {
  console.log('INP target element:', inpEntry.target);
  console.log('INP interaction type:', inpEntry.name);

  const navEntry = performance.getEntriesByType('navigation')[0];
  const wasINPBeforeDCL =
    inpEntry.startTime < navEntry.domContentLoadedEventStart;

  console.log('INP occurred before DCL:', wasINPBeforeDCL);
}

Ten en cuenta que este código no muestra cómo determinar qué entrada event es la entrada del INP, ya que esa lógica es más compleja. Sin embargo, en la siguiente sección, se explica cómo obtener esta información con la biblioteca web-vitals de JavaScript.

Uso con la biblioteca JavaScript de web-vitals

En las secciones anteriores, se ofrecen algunas sugerencias generales y ejemplos de código para capturar información de depuración a fin de incluirla en los datos que envías a tu herramienta de estadísticas.

A partir de la versión 3, la biblioteca JavaScript web-vitals incluye una compilación de atribución que muestra toda esta información, además de algunos indicadores adicionales.

En el siguiente ejemplo de código, se muestra cómo puedes configurar un parámetro de evento adicional (o una dimensión personalizada) que contenga una cadena de depuración útil para ayudar a identificar la causa raíz de los problemas de rendimiento.

import {onCLS, onFID, onINP, onLCP} from 'web-vitals/attribution';

function sendToGoogleAnalytics({name, value, id, attribution}) {
  const eventParams = {
    metric_value: value,
    metric_id: id,
  }

  switch (name) {
    case 'CLS':
      eventParams.debug_target = attribution.largestShiftTarget;
      break;
    case 'LCP':
      eventParams.debug_target = attribution.element;
      break;
    case 'FID':
    case 'INP':
      eventParams.debug_target = attribution.eventTarget;
      break;
  }

  // Assumes the global `gtag()` function exists, see:
  // https://developers.google.com/analytics/devguides/collection/ga4
  gtag('event', name, eventParams);
}

onCLS(sendToGoogleAnalytics);
onLCP(sendToGoogleAnalytics);
onFID(sendToGoogleAnalytics);
onINP(sendToGoogleAnalytics);

Este código es específico de Google Analytics, pero la idea general también debería traducirse en otras herramientas de estadísticas.

En este código, también se muestra cómo generar informes sobre un solo indicador de depuración, pero puede ser útil recopilar y generar informes sobre varios indicadores diferentes por métrica. Por ejemplo, para depurar el INP, es posible que desees recopilar el tipo de interacción, la hora y también el elemento con el que se interactúa. La compilación de atribución web-vitals expone toda esta información, como se muestra en el siguiente ejemplo:

import {onCLS, onFID, onINP, onLCP} from 'web-vitals/attribution';

function sendToGoogleAnalytics({name, value, id, attribution}) {
  const eventParams = {
    metric_value: value,
    metric_id: id,
  }

  switch (name) {
    case 'INP':
      eventParams.debug_target = attribution.eventTarget;
      eventParams.debug_type = attribution.eventType;
      eventParams.debug_time = attribution.eventTime;
      eventParams.debug_load_state = attribution.loadState;
      break;

    // Additional metric logic...
  }

  // Assumes the global `gtag()` function exists, see:
  // https://developers.google.com/analytics/devguides/collection/ga4
  gtag('event', name, eventParams);
}

onCLS(sendToGoogleAnalytics);
onLCP(sendToGoogleAnalytics);
onFID(sendToGoogleAnalytics);
onINP(sendToGoogleAnalytics);

Consulta la documentación de atribución de web-vitals para obtener la lista completa de los indicadores de depuración expuestos.

Genera informes y visualiza los datos

Una vez que hayas comenzado a recopilar información de depuración junto con los valores de las métricas, el siguiente paso es agregar los datos de todos tus usuarios para comenzar a buscar patrones y tendencias.

Como se mencionó anteriormente, no es necesario que resuelvas cada uno de los problemas que encuentran los usuarios. Debes abordar, especialmente al principio, los problemas que afectan a la mayor cantidad de usuarios, que también deberían ser los que tienen el mayor impacto negativo en tus puntuaciones de Métricas web esenciales.

Para GA4, consulta el artículo dedicado sobre cómo consultar y visualizar los datos con BigQuery.

Resumen

Esperamos que esta publicación haya ayudado a delinear las formas específicas en que puedes usar las APIs de rendimiento existentes y la biblioteca de web-vitals para obtener información de depuración y ayudar a diagnosticar el rendimiento en función de las visitas de usuarios reales en el campo. Si bien esta guía se centra en las Métricas web esenciales, los conceptos también se aplican a la depuración de cualquier métrica de rendimiento que se pueda medir en JavaScript.

Si recién comienzas a medir el rendimiento y ya eres usuario de Google Analytics, la herramienta del Informe de Métricas web puede ser un buen punto de partida, porque ya admite la generación de informes de información de depuración para las métricas Métricas web esenciales.

Si eres proveedor de estadísticas y deseas mejorar tus productos y proporcionar más información de depuración a los usuarios, considera algunas de las técnicas que se describen aquí, pero no te limites a solo las ideas que se presentan aquí. El propósito de esta publicación es que se aplique de manera general a todas las herramientas de estadísticas. Sin embargo, es probable que las herramientas de estadísticas individuales puedan (y deben) capturar y enviar aún más información de depuración.

Por último, si crees que hay brechas en tu capacidad para depurar estas métricas debido a funciones o información faltantes en las APIs, envía tus comentarios a web-vitals-feedback@googlegroups.com.