Depurar o desempenho no campo

Aprenda a atribuir seus dados de desempenho com informações de depuração para ajudar a identificar e corrigir problemas de usuários reais com análise

O Google oferece duas categorias de ferramentas para medir e depurar a performance:

  • Ferramentas do laboratório:ferramentas como o Lighthouse, em que a página é carregada em um ambiente simulado que pode imitar várias condições (por exemplo, uma rede lenta e um dispositivo móvel simples).
  • Ferramentas de campo:ferramentas como o Chrome User Experience Report (CrUX, na sigla em inglês), que se baseia em dados agregados de usuários reais do Chrome. Os dados de campo informados por ferramentas como PageSpeed Insights e Search Console são provenientes de dados do CrUX.

Embora as ferramentas de campo ofereçam dados mais precisos, que realmente representam a experiência real dos usuários, as ferramentas de laboratório geralmente são melhores para identificar e corrigir problemas.

Os dados do CrUX representam melhor o desempenho real da sua página, mas saber as pontuações do CrUX provavelmente não ajudará você a descobrir como melhorar a performance.

O Lighthouse, por outro lado, vai identificar problemas e fazer sugestões específicas sobre como melhorar. No entanto, o Lighthouse só fará sugestões para problemas de desempenho que detectar durante o carregamento da página. Ele não detecta problemas que só se manifestam como resultado de interações do usuário, como rolar ou clicar em botões na página.

Isso levanta uma pergunta importante: como coletar informações de depuração das Core Web Vitals ou outras métricas de desempenho de usuários reais em campo?

Esta postagem explica em detalhes quais APIs você pode usar para coletar mais informações de depuração para cada uma das métricas atuais das Core Web Vitals e dá ideias sobre como capturar esses dados na sua ferramenta de análise atual.

APIs para atribuição e depuração

CLS

De todas as Core Web Vitals, a CLS talvez seja a que é mais importante coletar informações de depuração em campo. A CLS é medida durante toda a vida útil da página. Portanto, a maneira como o usuário interage com a página (até onde rola, no que clica e assim por diante) pode ter um impacto significativo sobre a existência de mudanças de layout e quais elementos estão mudando.

Considere o seguinte relatório do PageSpeed Insights:

Um relatório do PageSpeed Insights com diferentes valores de CLS

O valor informado para CLS do laboratório (Lighthouse) em comparação com o CLS do campo (dados do CrUX) é muito diferente. Isso faz sentido se você considerar que a página pode ter 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 é necessário saber quais elementos da página estão mudando para resultar em uma pontuação de 0,3 no 75o percentil.

A interface LayoutShiftAttribution torna isso possível.

Acessar atribuição da troca de layout

A interface LayoutShiftAttribution é exposta em cada entrada layout-shift emitida pela API Layout Instability.

Para uma explicação detalhada das duas interfaces, consulte Depurar mudanças de layout. Para os fins desta postagem, o principal que você precisa saber é que, como desenvolvedor, você pode observar cada mudança de layout que acontece na página, assim como quais elementos estão mudando.

Confira um exemplo de código que registra cada mudança de layout, bem como os elementos que mudaram:

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

Provavelmente não é prático medir e enviar dados à sua ferramenta de análise para cada mudança de layout que ocorre. No entanto, ao monitorar todas as mudanças, é possível acompanhar as piores mudanças e relatar informações sobre elas.

O objetivo não é identificar e corrigir cada mudança de layout que ocorre para todos os usuários. O objetivo é identificar as mudanças que afetam o maior número de usuários e, assim, contribuem mais para a CLS da sua página no 75o percentil.

Além disso, você não precisa computar o maior elemento de origem sempre que há uma mudança. Você só precisa fazer isso quando estiver tudo pronto para enviar o valor do CLS para sua ferramenta de análise.

O código a seguir usa uma lista de entradas layout-shift que contribuíram para a CLS e retorna o maior elemento de origem da 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 maior elemento que contribui para a maior mudança, informe isso para sua ferramenta de análise.

O elemento que mais contribui para o CLS de uma determinada página provavelmente varia de usuário para usuário. No entanto, se você agregar esses elementos a todos os usuários, será possível gerar uma lista de elementos variáveis que afetam o maior número de usuários.

Depois que você identificar e corrigir a causa raiz das mudanças desses elementos, seu código de análise começará a informar mudanças menores como as "pior" das suas páginas. Eventualmente, todas as mudanças informadas serão pequenas o suficiente para que suas páginas estejam dentro do limite "satisfatório" de 0,1.

Alguns outros metadados que podem ser úteis para capturar com o maior elemento de origem de mudanças são:

  • A hora da maior mudança
  • O caminho do URL no momento da maior mudança (para sites que atualizam dinamicamente o URL, como aplicativos de página única).

LCP

Para depurar a LCP no campo, a informação principal que você precisa é de qual elemento específico foi o maior elemento (o elemento candidato a LCP) para esse carregamento de página específico.

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

Esse problema pode ocorrer por vários motivos:

  • Os dispositivos do usuário têm resoluções de tela diferentes, o que resulta em diferentes layouts de página e, assim, diferentes elementos visíveis na janela de visualização.
  • Os usuários nem sempre carregam páginas roladas para o topo. Muitas vezes, os links contêm identificadores de fragmentos ou até mesmo fragmentos de texto, o que significa que as páginas podem ser carregadas e exibidas em qualquer posição de rolagem.
  • O conteúdo pode ser personalizado para o usuário atual, de modo que o elemento de candidato à LCP pode variar muito de usuário para usuário.

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

Identifique o elemento candidato à LCP

Para determinar o elemento candidato à LCP no JavaScript, use a API Largest Contentful Paint, a mesma API usada para determinar o valor do tempo da LCP.

Ao observar entradas largest-contentful-paint, é possível determinar o elemento atual do candidato à LCP observando a propriedade 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});

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

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

FID

Para depurar o FID no campo, é importante lembrar que ela mede apenas a parte de atraso da latência geral do primeiro evento de entrada. Isso significa que a interação do usuário não é tão importante quanto o que mais estava acontecendo na linha de execução principal no momento da interação.

Por exemplo, muitos aplicativos JavaScript com suporte à renderização do lado do servidor (SSR, na sigla em inglês) entregam HTML estático que pode ser renderizado na tela antes de ser interativo com a entrada do usuário, ou seja, antes que o JavaScript necessário para tornar o conteúdo interativo seja carregado.

Para esses tipos de aplicativos, pode ser muito importante saber se a primeira entrada ocorreu antes ou depois da hidratação. Se muitas pessoas estiverem tentando interagir com a página antes da conclusão da hidratação, considere renderizar as páginas no estado desativado ou de carregamento, em vez de em um estado que pareça interativo.

Se o framework do aplicativo expõe o carimbo de data/hora de hidratação, é possível comparar isso com o da entrada first-input para determinar se a primeira entrada aconteceu antes ou depois da hidratação. Se o framework não expor esse carimbo de data/hora ou não usar hidratação, outro sinal útil poderá ser se a entrada ocorreu antes ou depois do carregamento do JavaScript.

O evento DOMContentLoaded é disparado depois que o HTML da página for 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, é possível usar o tempo desse evento e compará-lo com o momento em que a FID ocorreu.

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

Identificar o elemento de destino da FID e o tipo de evento

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

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

Para saber o elemento e o tipo de interação associados ao primeiro evento de entrada, consulte as propriedades target e name da 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

O INP é muito semelhante à FID, porque as informações mais úteis para capturar em campo são:

  1. Com o elemento que teve a interação
  2. Por que o tipo de interação foi
  3. Quando essa interação ocorreu

Assim como a FID, uma das principais causas de interações lentas é uma linha de execução principal bloqueada, o que pode ser comum durante o carregamento do JavaScript. Saber se a maioria das interações lentas ocorre 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 qualquer listener de eventos registrados, bem como o tempo necessário para mostrar o próximo frame depois que todos os listeners de eventos forem executados. Isso significa que, para o INP, é ainda mais útil saber quais elementos de destino tendem a resultar em interações lentas e quais tipos de interações são essas.

Como o INP e o 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);
}

Esse código não mostra como determinar qual entrada event é a entrada INP, já que essa lógica está mais envolvida. No entanto, na seção a seguir, explicamos como conseguir 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 e incluir nos dados enviados à sua ferramenta de análise.

Desde a versão 3, a biblioteca JavaScript web-vitals inclui um build de atribuição que apresenta todas essas informações e alguns outros indicadores.

O exemplo de código a seguir mostra como 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_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);

Esse código é específico para o Google Analytics, mas a ideia geral também precisa ser implementada em outras ferramentas de análise.

Esse código também mostra apenas como gerar relatórios sobre um único indicador de depuração, mas pode ser útil coletar e gerar relatórios sobre vários indicadores diferentes por métrica. Por exemplo, para depurar INP, você pode coletar o tipo de interação, o horário e o elemento com que está interagindo. O build de atribuição web-vitals expõe todas essas informações, como 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.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);

Consulte a documentação de atribuição web-vitals para ver a lista completa de indicadores de depuração expostos.

Gerar relatórios e visualizar os dados

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

Como mencionado acima, você não precisa necessariamente resolver todos os problemas que os usuários estão enfrentando. Você quer resolver, especialmente no início, os problemas que estão afetando o maior número de usuários, que também devem ser os problemas que têm o maior impacto negativo nas suas pontuações das Core Web Vitals.

Para o GA4, consulte o artigo dedicado sobre como consultar e visualizar os dados usando o BigQuery.

Resumo

Esperamos que esta postagem tenha ajudado a descrever as maneiras específicas de usar as APIs de desempenho existentes e a biblioteca web-vitals para receber informações de depuração e diagnosticar a performance com base nas visitas de usuários reais em campo. Embora este guia se concentre nas Core Web Vitals, os conceitos também se aplicam à depuração de qualquer métrica de desempenho mensurável em JavaScript.

Se você está começando a avaliar a performance e já é usuário do Google Analytics, a ferramenta de relatórios de Métricas da Web pode ser um bom ponto de partida, porque já oferece suporte a relatórios de informações de depuração para as Core Web Vitals.

Se você for um fornecedor de análises e quiser melhorar seus produtos e fornecer mais informações de depuração aos usuários, use algumas das técnicas descritas aqui, mas não se limite a apenas as ideias apresentadas aqui. O objetivo desta postagem é ser aplicável a todas as ferramentas de análise. No entanto, cada ferramenta de análise provavelmente pode (e precisa) capturar e relatar ainda mais informações de depuração.

Por fim, se você achar que ainda precisa depurar essas métricas devido à ausência de recursos ou informações nas próprias APIs, envie seu feedback para web-vitals-feedback@googlegroups.com.