Depurar o desempenho no campo

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

O Google oferece duas categorias de ferramentas para medir e depurar o desempenho:

  • 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 Relatório (CrUX, na sigla em inglês), que se baseia em dados agregados e reais de usuários do Chrome. Os dados de campo informados por ferramentas como PageSpeed Insights e Search Console são extraídos dos dados do CrUX.

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

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

Por outro lado, o Lighthouse identificará problemas e fará sugestões específicas sobre como melhorar. No entanto, o Lighthouse só fará sugestões para problemas de desempenho descobertos no tempo de carregamento da página. Ele não detecta problemas que só se manifestam como resultado da interação 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 para Core Web Vitals ou outras métricas de desempenho de usuários reais em campo?

Esta postagem explica em detalhes quais APIs podem ser usadas para coletar informações de depuração adicionais para cada uma das métricas atuais das Core Web Vitals e oferece ideias de como capturar esses dados na sua ferramenta de análise atual.

APIs para atribuição e depuração

CLS

De todas as métricas das Core Web Vitals, a CLS é talvez aquela em que a coleta de informações de depuração em campo é a mais importante. A CLS é medida durante toda a vida útil da página. Portanto, a maneira como um usuário interage com a página (quando rola a página, no que clica e assim por diante) pode ter um impacto significativo sobre as 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, e 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.

No entanto, mesmo que você entenda que a interação do usuário afeta os dados de campo, ainda é preciso 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.

Receber a atribuição da mudança de layout

A interface LayoutShiftAttribution é exposta em cada entrada de layout-shift que a API Layout Instability emite.

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

Veja um exemplo de código 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});

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 simplesmente gerar relatórios sobre elas.

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

Além disso, não é necessário computar o maior elemento de origem sempre que há uma mudança. Faça isso apenas quando estiver tudo pronto para enviar o valor da 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 à 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. No entanto, se você agregar esses elementos em todos os usuários, será possível gerar uma lista de elementos móveis que 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 registrar mudanças menores como as "piores" das suas páginas. Com o tempo, todas as mudanças informadas serão pequenas o suficiente para que suas páginas estejam dentro do limite "bom" de 0,1.

Alguns outros metadados que podem ser úteis para capturar junto com o maior elemento de origem de mudança 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 de que você precisa é qual elemento específico foi o maior elemento (o elemento candidato à LCP) para esse carregamento de página específico.

É possível que o elemento do 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 dos usuários têm resoluções de tela diferentes, o que resulta em layouts de página distintos e, portanto, elementos distintos 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 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 do usuário real.

Identificar o elemento candidato à LCP

Para determinar o elemento candidato à LCP em 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, você pode determinar o elemento atual de candidato à LCP analisando 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 saber o elemento de candidato à LCP, você poderá enviá-lo para sua ferramenta de análise com o valor da métrica. Assim como no 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 períodos de subpartes da LCP, o que pode ser útil para determinar quais etapas de otimização específicas são relevantes para seu site.

FID

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

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

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 em um 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. Caso o framework não exponha esse carimbo de data/hora ou não use a hidratação, outro indicador útil pode ser se a entrada ocorreu antes ou depois do carregamento do JavaScript.

O evento DOMContentLoaded é disparado depois que o HTML da página é 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 as entradas de first-input e registra 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 de 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). A interação com o elemento em si não contribui 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 ocorre com um elemento específico, considere inserir o código JavaScript necessário para esse elemento no HTML e carregar lentamente o restante.

Para conferir o tipo de interação e o elemento associado 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 a serem capturadas em campo são:

  1. Com qual elemento a interação foi feita
  2. Por que o tipo de interação foi
  3. Quando essa interação aconteceu

Assim como a FID, uma das principais causas de interações lentas é uma linha de execução principal bloqueada, 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 os listeners de eventos registrados e o tempo necessário para pintar o próximo frame após a execução de todos os listeners de eventos. Isso significa que, para o INP, é ainda mais útil saber quais elementos de destino tendem a resultar em interações lentas e que tipos de interações essas são.

Como o INP e o FID são baseados na API Event Timing, a forma como você determina essas informações em JavaScript é muito semelhante ao exemplo anterior. O código a seguir registra o elemento de destino e a hora (em relação 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, a seção a seguir explica 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 à ferramenta de análise.

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

O exemplo de código a seguir mostra como definir um outro parâmetro de evento (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 aplicada a 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 tempo e o elemento com que está interagindo. O build de atribuição web-vitals expõe todas essas informações, conforme mostrado no exemplo abaixo:

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 conferir 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, não é necessário resolver todos os problemas encontrados pelos usuários. Você quer resolver, especialmente no início, os problemas que afetam o maior número de usuários, além de aqueles que têm o maior impacto negativo nas 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 atuais e a biblioteca web-vitals para acessar informações de depuração e diagnosticar o desempenho 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 o desempenho e já é um 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 oferecer mais informações de depuração aos usuários, considere algumas das técnicas descritas aqui, mas não se limite apenas às ideias apresentadas aqui. Esta postagem destina-se a ser aplicável a todas as ferramentas de análise. No entanto, ferramentas de análise individuais provavelmente podem (e precisam) capturar e relatar ainda mais informações de depuração.

Por fim, se você achar que há lacunas na depuração dessas métricas devido à ausência de recursos ou informações nas APIs, envie seu feedback para web-vitals-feedback@googlegroups.com.