Debugando Core Web Vitals com dados de usuários

Como melhorar sua poontuação no Web Vitals com informações em tempo real


Autor: Réulison Silva Publicado em: Fevereiro 15, 2023

Saiba como melhorar seus dados do Web Vitals com informações em tempo real para ajudá-lo a identificar e corrigir problemas de usuários.

Atualmente, o Google fornece duas categorias de ferramentas para medir e depurar Web Vitals:

  • Ferramentas de laboratório: ferramentas como o Lighthouse, onde sua página é carregada em um ambiente simulado que pode imitar várias condições (por exemplo, uma rede lenta e um dispositivo móvel de baixo custo).

  • Ferramentas de campo: ferramentas como o Chrome UX Report(CrUX), que é baseado em dados agregados de usuários reais do Chrome. (Observe que os dados de campo relatados por ferramentas como o PageSpeed Insights e o Search Console são provenientes de dados do CrUX .)

Embora as ferramentas de campo ofereçam dados mais precisos – dados que realmente representam o que os usuários reais experimentam – as ferramentas de laboratório geralmente são melhores para ajudá-lo a identificar e corrigir problemas.

Os dados do CrUX são mais representativos do desempenho real da sua página, mas é improvável que mesmo conhecendo suas pontuações do CrUX consiga descobrir como melhorar seu desempenho.

O Lighthouse, por outro lado, identifica os problemas e te dá sugestões específicas de como melhorar, como rolar ou clicar nos botões da página

Isso levanta uma questão importante: como você pode capturar informações de debug do Web Vitals de usuários reais?

Vou tentar explicar quais APIs você pode usar para coletar informações adicionais de debug para cada uma das métricas atuais do Core Web Vitals e fornecerá sugestões sobre como capturar esses dados em sua ferramenta de análise existente.

APIs para atribuição e depuração

CLS

De todas as métricas do Core Web Vitals, o CLS é talvez aquele para o qual a coleta de informações de debug é a mais importante. Eventos de scroll, cliques e assim por diante - podem ter um impacto significativo sobre mudanças de layout e quais elementos estão mudando.

Considere o seguinte relatório do PageSpeed Insights para a URL: reulison.com.br

Core Web Vitals Assessment
Core Web Vitals Perfomance

O valor informado para o CLS do laboratório (Lighthouse) comparado ao CLS do campo (dados do CrUX) é bem diferente, e isso faz sentido se você considerar que a página reulison.com.br tem muito conteúdo interativo que não está sendo usado quando testado no Lighthouse.

Mas mesmo que você entenda que a interação do usuário afeta os dados de campo, ainda precisa saber quais elementos na página estão mudando para resultar em uma pontuação de 0,14 no CLS.

A interface LayoutShiftAttribution (outra hora eu detalho isso melhor) torna isso possível.

Entendendo a atribuição de mudança de layout

A interface LayoutShiftAttribution é exposta em cada entrada de mudança de layout que a API de instabilidade de layout emite.

Para os propósitos deste artigo o que você precisa saber é que, como desenvolvedor, você pode observar cada mudança de layout que acontece na página, bem como quais elementos estão mudando.

Aqui está um código de exemplo que registra cada mudança de layout, bem como os elementos que foram alterados:

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});

Não é muito prático medir e enviar dados para sua ferramenta de análise para cada layout-shift que ocorre; no entanto, monitorando todas as mudanças, você pode acompanhar as piores e apenas relatar informações sobre essas.

O objetivo não é identificar e corrigir cada layout-shift que ocorre para cada usuário, o objetivo é identificar as mudanças que afetam o maior número de usuários e, assim, contribuem mais para o CLS da sua página.

Além disso, você não precisa computar o elemento toda vez que houver uma mudança, você só precisa fazer isso quando estiver pronto para enviar o valor CLS para sua ferramenta de análise.

O código a seguir obtém uma lista de entradas de layout-shift que contribuíram para o CLS e retorna o elemento com gera maior mudança:

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;
   }
 }
}

Depois de identificar o elemento que contribui para a maior mudança, você pode relatar isso à sua ferramenta de análise.

O elemento que mais contribui para o CLS para uma determinada página provavelmente varia de usuário para usuário, mas se você agregar esses elementos em todos os usuários, poderá gerar uma lista de elementos que geram grandes mudanças no layout e afetam o maior número de usuários.

Depois de identificar e corrigir a causa raiz das mudanças desses elementos, seu código de análise começará a relatar mudanças menores como as maiores mudanças de layout. Eventualmente, todas as mudanças relatadas serão pequenas o suficiente para que suas páginas fiquem bem dentro do limite “bom” de 0,1!

Alguns outros metadados que podem ser úteis para capturar junto com o layout-shift são:

  • A hora do layout-shift.
  • A URL com maior mudança de layout (para sites que atualizam dinamicamente a URL, como Single Page Applications).

LCP

Para depurar o LCP com dados de usuários a principal informação é qual foi o maior elemento para aquele carregamento de página específico.

Observe que é inteiramente possível — na verdade, é bastante comum — que o elemento candidato do LCP seja diferente de usuário para usuário, mesmo para a mesma página.

Isso pode acontecer por vários motivos:

  • Os dispositivos do usuário têm diferentes resoluções de tela, o que resulta em diferentes layouts de página e, portanto, em diferentes elementos visíveis na janela de visualização.
  • Os usuários nem sempre carregam páginas que começam no topo. Frequentemente, os links irão conter identificadores de fragmentos ou mesmo fragmentos de texto, o que significa que é possível que suas páginas sejam carregadas e exibidas em qualquer posição na página.
  • O conteúdo pode ser personalizado para o usuário atual, portanto, o elemento candidato para LCP pode variar muito de usuário para usuário.

Isso significa que você não pode fazer suposições sobre qual elemento ou conjunto de elementos será o elemento candidato para LCP mais comum para uma página específica. Você deve medi-lo com base no comportamento do usuário real.

Identifique o elemento candidato LCP

Para determinar o elemento candidato LCP em JavaScript, você pode usar a API Largest Contentful Paint, a mesma API usada para determinar o valor de tempo LCP.

Ao observar as entradas de largest-contentful-paint, você pode determinar o elemento candidato LCP atual observando a propriedade do element da ú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});

⚠ Cuidado Conforme explicado na documentação da métrica LCP, o elemento candidato LCP pode mudar durante o carregamento da página, portanto, é necessário mais trabalho para identificar o elemento candidato LCP “final”. A maneira mais fácil de identificar e medir o elemento candidato LCP “final” é usar a biblioteca JavaScript web-vitals, conforme será mostrado no exemplo abaixo.

Depois de conhecer o elemento candidato ao LCP, você pode enviá-lo para sua ferramenta de análise junto com o valor da métrica. Assim como no CLS, isso o ajudará a identificar quais elementos são mais importantes para otimizar primeiro.

Além do elemento candidato do LCP, também pode ser útil medir os tempos das subpartes do LCP, o que pode ser útil para determinar quais etapas de otimização específicas são relevantes para seu site.

FID

Para depurar o FID, é importante lembrar que o FID mede apenas a parte do atraso da latência geral do primeiro evento de entrada. Isso significa que o que o usuário interagiu não é realmente tão importante quanto o que mais estava acontecendo no thread principal no momento em que ele interagiu.

Por exemplo, muitos aplicativos JavaScript que oferecem suporte à renderização do lado do servidor (SSR) fornecerão um HTML estático que pode ser renderizado na tela antes da página se tornar interativa, ou seja, antes que o JavaScript necessário para tornar o conteúdo interativo seja carregado.

Para esses tipos de aplicações, pode ser muito importante saber se a primeira entrada ocorreu antes ou depois da hydration. Se muitas pessoas estiverem tentando interagir com a página antes que a hydration seja concluída, considere renderizar suas páginas em um estado desativado ou de carregamento, em vez de em um estado que pareça interativo.

Se a estrutura do seu aplicativo expõe o registro de data e hora de hydration, você pode compará-lo facilmente com o registro de data e hora da first-input para determinar se a primeira entrada ocorreu antes ou depois da hydration. Se sua estrutura não expõe o registro de data e hora ou não usa hydration, outro sinal útil pode ser se a entrada que ocorreu antes ou depois do carregamento do JavaScript.

O evento DOMContentLoaded é acionado após o HTML da página ter sido completamente carregado e analisado, o que inclui aguardar o carregamento de qualquer script síncrono, adiado ou de módulo (incluindo todos os módulos importados estaticamente). Assim, você pode usar o tempo desse evento e compará-lo com quando ocorreu o FID.

O código a seguir observa o first-input e registra se a primeira entrada ocorreu ou não antes do final do 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});

💡 Se sua página usa scripts async ou import() para carregar JavaScript, o evento DOMContentLoaded pode não ser um sinal muito bom. Em vez disso, você pode considerar o uso do evento load ou - se houver um script específico que você sabe que demora um pouco para ser executado - você pode usar a entrada Resource Timing para esse script.

Identifique o elemento de destino do FID e o tipo de evento

Sinais de depuração potencialmente úteis são os elementos com o qual interagiu, bem como o tipo de interação (como mousedown, keydown, pointerdown). Embora a interação com o elemento em si não contribua para o FID (lembre-se de que o FID é apenas a parte de atraso da latência total do evento), saber com quais elementos seus usuários estão interagindo pode ser útil para determinar a melhor forma de melhorar o FID.

Por exemplo, se a grande maioria das primeiras interações do usuário for com um elemento específico, considere inserir o código JavaScript necessário para esse elemento no HTML e carregar lentamente o restante.

Para obter o tipo de interação e o elemento associado ao primeiro evento de entrada, você pode fazer referência às propriedades target e name do 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

O INP é muito semelhante ao FID, pois são os dados de informação mais úteis do usuários são:

  • Com qual elemento ocorreu a interação;
  • Que tipo de interação ocorreu;
  • Quando essa interação ocorreu.

Assim como o FID, uma das principais causas de interações lentas é uma thread principal bloqueada, o que pode ser comum durante o carregamento do JavaScript. Saber se as interações mais lentas ocorrem durante o carregamento da página é útil para determinar o que precisa ser feito para corrigir o problema.

Ao contrário do FID, a métrica INP considera a latência total de uma interação, incluindo o tempo necessário para executar quaisquer listeners de eventos registrados, bem como o tempo necessário para exibir o próximo quadro após a execução de todos os listeners. Isso significa que, para o INP, é ainda mais útil saber quais elementos tendem a resultar em interações lentas e quais são esses tipos de interação.

Como INP e FID são baseados na API Event Timing, a maneira como você determina essas informações em JavaScript é muito semelhante ao exemplo anterior. O código a seguir registra o elemento de destino e o tempo (relativo a DOMContentLoaded) da entrada 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);
}

Observe que esse código não mostra como determinar qual event é a entrada INP, pois essa lógica é mais complexa. No entanto, a seção a seguir explica como obter essas informações usando a biblioteca JavaScript web-vitals.

Uso com a biblioteca JavaScript web-vitals

As seções acima oferecem algumas sugestões gerais e exemplos de código para capturar informações de depuração para incluir nos dados que você envia para sua ferramenta de análise.

Desde a versão 3, a biblioteca JavaScript web-vitals inclui uma construção de atribuição que apresenta todas essas informações e também alguns sinais adicionais.

O exemplo de código a seguir mostra como você pode definir um parâmetro de evento adicional (ou dimensão personalizada) contendo uma string de depuração útil para ajudar a identificar a causa raiz dos problemas de desempenho.

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_string = attribution.largestShiftTarget;
     break;
   case 'LCP':
     eventParams.debug_string = attribution.element;
     break;
   case 'FID':
   case 'INP':
     eventParams.debug_string = 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 é específico para o Google Analytics, mas a ideia geral também deve servir facilmente para outras ferramentas de análise.

Este código também mostra como relatar um único sinal de depuração, mas pode ser útil coletar e relatar vários sinais diferentes por métrica. Por exemplo, para depurar o INP, você pode querer coletar o tipo de interação, o tempo e também o elemento com o qual está interagindo. A compilação de atribuição web-vitals expõe todas essas informações, conforme mostrado no exemplo a seguir:

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.inp_target = attribution.eventTarget;
     eventParams.inp_type = attribution.eventType;
     eventParams.inp_time = attribution.eventTime;
     eventParams.inp_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);

Consulte a documentação de atribuição do web-vitals para obter a lista completa de sinais de depuração.

Reporte e visualize os dados

Depois de começar a coletar informações de depuração junto com os valores de métrica, a próxima etapa é agregar os dados de todos os seus usuários para começar a procurar padrões e tendências.

Conforme mencionado acima, você não precisa necessariamente resolver todos os problemas que seus usuários estão encontrando, você deseja abordar, especialmente no início, os problemas que afetam o maior número de usuários, que também devem ser os problemas que têm o maior impacto negativo em suas pontuações no Core Web Vitals.

A ferramenta Web Vitals Report

Se você estiver usando a ferramenta Web Vitals Report, ela também permite que você crie relatórios sobre uma única dimensão de depuração para cada uma das métricas de Core Web Vitals.

Aqui está uma captura de tela da seção de informações do Web Vitals Report, mostrando os dados do próprio site da ferramenta Web Vitals Report:

Core Web Vitals Report

Usando os dados acima, você pode ver que tudo o que está causando a mudança do elemento section.Intro é o que mais contribui para o CLS nesta página, portanto, identificar e corrigir a causa dessa mudança irá melhorar sua pontuação.

Espero que este artigo tenha ajudado a descrever algumas maneiras específicas de usar as APIs de desempenho existentes e a biblioteca do web-vitals para obter informações de depuração para ajudar a diagnosticar o desempenho com base em visitas de usuários reais. Embora este artigo se concentre no Core Web Vitals, os conceitos também se aplicam à depuração de qualquer métrica de desempenho mensurável em JavaScript.

Se você está apenas começando a medir o desempenho e já é um usuário do Google Analytics, a ferramenta Web Vitals Report pode ser um bom lugar para começar porque ela já oferece suporte a relatórios de informações de depuração para as métricas de Core Web Vitals.

Se você é um SEO e deseja fornecer mais informações de depuração para seus clientes, considere algumas das técnicas descritas aqui, mas não se limite apenas às ideias apresentadas aqui. Este artigo destina-se a ser geralmente aplicável a todas as ferramentas analíticas; no entanto, as ferramentas analíticas individuais provavelmente podem (e devem) capturar e relatar ainda mais informações de depuração.