Core Web Vitals: guia técnico para melhorar LCP, CLS e INP

1. Fundamentos dos Core Web Vitals e seu Impacto no SEO e UX

Os Core Web Vitals são um conjunto de métricas definidas pelo Google que medem aspectos fundamentais da experiência do usuário na web. As três métricas principais são:

  • Largest Contentful Paint (LCP): mede o tempo de carregamento do maior elemento visível na janela de visualização. Ideal: ≤ 2,5 segundos.
  • Cumulative Layout Shift (CLS): quantifica mudanças inesperadas de layout. Ideal: ≤ 0,1.
  • Interaction to Next Paint (INP): avalia a capacidade de resposta a interações do usuário. Ideal: ≤ 200 milissegundos.

Essas métricas impactam diretamente o ranqueamento em buscadores, a taxa de rejeição e a satisfação do usuário. Ferramentas como Lighthouse, PageSpeed Insights, Chrome UX Report e a Web Vitals API são essenciais para medição e diagnóstico.

2. Largest Contentful Paint (LCP): Otimização do Maior Elemento Visível

Identificação do elemento LCP

Para detectar programaticamente o elemento LCP, utilize a Performance Observer API:

const observer = new PerformanceObserver((list) => {
  const entries = list.getEntries();
  const lastEntry = entries[entries.length - 1];
  console.log('Elemento LCP:', lastEntry.element);
  console.log('Tempo LCP:', lastEntry.startTime);
});
observer.observe({ type: 'largest-contentful-paint', buffered: true });

Técnicas de carregamento prioritário

Atribua fetchpriority="high" ao elemento LCP identificado:

<img src="hero.webp" fetchpriority="high" alt="Imagem principal" />

Pré-carregue fontes críticas no <head>:

<link rel="preload" href="/fonts/inter-var.woff2" as="font" type="font/woff2" crossorigin>

Redução de tempo de resposta do servidor

O TTFB (Time to First Byte) deve ser inferior a 800ms. Implemente:

  • CDN com edge caching
  • Compressão Brotli ou Gzip
  • Cache de banco de dados e Redis
  • Otimização de consultas SQL

3. Cumulative Layout Shift (CLS): Eliminando Mudanças Repentinas de Layout

Causas comuns de CLS

  • Imagens sem dimensões explícitas
  • Anúncios e embeds que inserem conteúdo após o carregamento
  • Fontes web que causam reflow (FOUT/FOIT)
  • Iframes sem altura definida

Boas práticas de dimensionamento

Sempre declare width e height em imagens e use aspect-ratio no CSS:

<img src="banner.jpg" width="1200" height="630" alt="Banner" style="aspect-ratio: 1200/630" />

Para elementos responsivos, use placeholders proporcionais:

<div style="padding-bottom: 56.25%; position: relative;">
  <img src="video-thumbnail.jpg" style="position: absolute; width: 100%; height: 100%;" />
</div>

Estratégias para conteúdo dinâmico

Reserve espaço para anúncios com dimensões fixas:

<div class="ad-container" style="min-height: 250px; width: 300px;">
  <!-- Anúncio será inserido aqui -->
</div>

Animações devem usar transform em vez de propriedades que afetam layout:

/* Ruim: causa CLS */
.element { animation: slide 1s; }
@keyframes slide { from { margin-left: -100px; } to { margin-left: 0; } }

/* Bom: não causa CLS */
.element { animation: slide 1s; }
@keyframes slide { from { transform: translateX(-100px); } to { transform: translateX(0); } }

4. Interaction to Next Paint (INP): Melhorando a Capacidade de Resposta a Interações

Entendendo o INP

O INP mede o tempo desde a interação do usuário (clique, toque, tecla) até o próximo frame pintado. Use a Web Vitals API para monitorar:

import { onINP } from 'web-vitals';

onINP((metric) => {
  console.log('INP:', metric.value, 'ms');
  // Enviar para analytics
});

Otimização do thread principal

Divida tarefas longas (>50ms) usando setTimeout ou requestIdleCallback:

function processLargeArray(items) {
  const chunkSize = 100;
  let index = 0;

  function processChunk() {
    const end = Math.min(index + chunkSize, items.length);
    for (let i = index; i < end; i++) {
      // Processar item
    }
    index = end;
    if (index < items.length) {
      setTimeout(processChunk, 0);
    }
  }
  processChunk();
}

Utilize Web Workers para processamento pesado:

// main.js
const worker = new Worker('worker.js');
worker.postMessage({ data: largeDataset });
worker.onmessage = (event) => {
  // Processar resultado
};

// worker.js
self.onmessage = (event) => {
  const result = heavyComputation(event.data);
  self.postMessage(result);
};

Redução de bloqueio de renderização

Implemente debouncing em eventos de alta frequência:

function debounce(fn, delay = 100) {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => fn(...args), delay);
  };
}

window.addEventListener('scroll', debounce(() => {
  // Lógica de scroll
}, 50));

Use virtual scrolling para listas grandes:

// Implementação simplificada de virtual scrolling
const container = document.querySelector('.scroll-container');
const itemHeight = 50;
const totalItems = 10000;

container.addEventListener('scroll', () => {
  const startIndex = Math.floor(container.scrollTop / itemHeight);
  const endIndex = startIndex + Math.ceil(container.clientHeight / itemHeight) + 1;
  renderVisibleItems(startIndex, endIndex);
});

5. Estratégias Avançadas de Performance para LCP

Otimização de imagens

Converta para formatos modernos e use CDN com transformação dinâmica:

<picture>
  <source srcset="image.avif" type="image/avif">
  <source srcset="image.webp" type="image/webp">
  <img src="image.jpg" fetchpriority="high" width="800" height="450" alt="Otimizada">
</picture>

Carregamento crítico de CSS

Faça inlining do CSS acima da dobra e adie o restante:

<style>
  /* CSS crítico para o conteúdo acima da dobra */
  .hero { display: flex; align-items: center; ... }
</style>
<link rel="preload" href="/styles/full.css" as="style" onload="this.onload=null;this.rel='stylesheet'">

Server-Side Rendering (SSR)

Entregue HTML pré-renderizado para reduzir o tempo de LCP:

// Exemplo com Next.js
export async function getServerSideProps() {
  const data = await fetchAPI();
  return { props: { data } };
}

6. Monitoramento Contínuo e Automação de Métricas

Configuração de Web Vitals API

Colete dados reais de usuários (RUM) no front-end:

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

function sendToAnalytics(metric) {
  const body = JSON.stringify({
    name: metric.name,
    value: metric.value,
    rating: metric.rating,
    delta: metric.delta,
    id: metric.id
  });
  navigator.sendBeacon('/analytics', body);
}

onLCP(sendToAnalytics);
onCLS(sendToAnalytics);
onINP(sendToAnalytics);

Integração com ferramentas de observabilidade

Configure alertas de regressão em plataformas como Datadog ou Sentry:

// Exemplo com Sentry
import * as Sentry from '@sentry/react';

onLCP((metric) => {
  if (metric.rating === 'poor') {
    Sentry.captureMessage('LCP degradado', { extra: metric });
  }
});

Pipeline de CI/CD com Lighthouse CI

Garanta que mudanças não degradem as métricas antes do deploy:

# lighthouse-ci.config.js
module.exports = {
  ci: {
    collect: {
      url: ['https://example.com'],
      numberOfRuns: 3
    },
    assert: {
      assertions: {
        'largest-contentful-paint': ['warn', { maxNumericValue: 2500 }],
        'cumulative-layout-shift': ['warn', { maxNumericValue: 0.1 }],
        'interaction-to-next-paint': ['warn', { maxNumericValue: 200 }]
      }
    },
    upload: {
      target: 'temporary-public-storage'
    }
  }
};

Execute no CI:

npx lhci autorun

Referências

  • Web Vitals - Google Developers — Documentação oficial sobre Core Web Vitals, incluindo definições, métricas e boas práticas
  • Optimize LCP - web.dev — Guia completo para otimização do Largest Contentful Paint com exemplos práticos
  • Optimize CLS - web.dev — Estratégias detalhadas para eliminar mudanças de layout e melhorar o Cumulative Layout Shift
  • Optimize INP - web.dev — Tutorial técnico sobre como melhorar o Interaction to Next Paint com técnicas de otimização do thread principal
  • Lighthouse CI - GitHub — Ferramenta oficial do Google para automação de auditorias Lighthouse em pipelines de CI/CD
  • Web Vitals API - MDN Web Docs — Referência técnica da API Performance Observer para coleta de métricas reais de usuários
  • PageSpeed Insights — Ferramenta oficial do Google para análise de performance com recomendações específicas para Core Web Vitals