Por que a performance é uma funcionalidade
Performance não é um passo de polimento ao final de um projeto. Ela decide se os usuários chegarão a ver o seu conteúdo. Uma página que leva quatro segundos para exibir seu conteúdo principal perde uma grande parcela de visitantes antes mesmo de se tornar utilizável, e interações lentas fazem com que um app pareça quebrado, mesmo quando está funcionando.
A boa notícia é que a performance é mensurável e, em grande parte, sistemática. Um pequeno número de alavancas — carregar menos, carregar mais tarde, realizar menos trabalho na main thread — representa a vasta maioria dos ganhos. Este guia aborda as métricas, as ferramentas e as técnicas que mais importam.
Core Web Vitals
Três métricas, chamadas coletivamente de Core Web Vitals, descrevem a experiência do usuário:
- Largest Contentful Paint (LCP) — quando o maior elemento visível termina de ser renderizado. Um bom resultado é abaixo de 2,5 segundos.
- Interaction to Next Paint (INP) — a latência entre uma interação do usuário e a próxima atualização visual. Um bom resultado é abaixo de 200 milissegundos.
- Cumulative Layout Shift (CLS) — o quanto o conteúdo visível se move inesperadamente. Um bom resultado é abaixo de 0,1.
Métricas complementares incluem o Time to First Byte (TTFB) para latência do servidor e o First Contentful Paint (FCP) para quando o primeiro conteúdo aparece. Juntas, elas indicam se uma página é rápida, responsiva e estável.
Medição
Você precisa tanto de field data quanto de lab data.
O field data vem de usuários reais e reflete a verdadeira gama de dispositivos e redes. É para isso que você deve otimizar. Já o lab data do Lighthouse ou DevTools é reproduzível, o que o torna melhor para diagnosticar um problema específico.
O navegador expõe tempos reais através da Performance API.
// observe.js
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(entry.name, entry.startTime);
}
}).observe({ type: "largest-contentful-paint", buffered: true });
Bibliotecas como web-vitals encapsulam esses observers e reportam para a sua análise de dados, o que fornece field data sem que você precise construir toda a infraestrutura do zero.
Carregando menos
A alavanca mais importante de todas é enviar menos código. O JavaScript é o recurso mais caro porque precisa ser baixado, analisado e executado, competindo com a renderização pela thread principal.
- Remova dependências não utilizadas e prefira alternativas menores.
- Importe apenas as funções que você realmente usa de uma biblioteca.
- Divida rotas e funcionalidades pesadas com
import()dinâmicos. - Renderize no servidor sempre que possível e envie apenas as partes interativas.
- Comprima e sirva formatos modernos, como Brotli e AVIF.
- Defina um bundle budget e faça o CI falhar quando ele for ultrapassado.
Um bundle analyser mostra exatamente o que está no seu build, o que geralmente é surpreendente na primeira vez que você olha.
Carregando posteriormente
Nem tudo é necessário para a primeira renderização. Adie o restante.
<!-- deferred.js -->
<script src="app.js" defer></script>
<script src="analytics.js" async></script>
<img src="hero.avif" fetchpriority="high" alt="Hero" />
<img src="thumb.avif" loading="lazy" decoding="async" width="400" height="300" alt="" />
defer executa scripts após o parsing, async os executa assim que são baixados, fetchpriority="high" prioriza a imagem do LCP, e loading="lazy" adia imagens fora da tela. import() dinâmico faz o mesmo para módulos JavaScript.
Renderização e estabilidade
Dois problemas de renderização dominam o CLS e a percepção de velocidade.
Reserve espaço para imagens, iframes, anúncios e embeds para que eles não desloquem o conteúdo. Defina width e height, ou use aspect-ratio no CSS. Utilize uma estratégia de fontes que evite a troca tardia (late swap), como font-display: swap com um fallback correspondente ou fontes self-hosted.
Mantenha a main thread livre. Tarefas longas bloqueiam a interação e aumentam o INP. Fragmente trabalhos onerosos, mova computações pesadas para um Web Worker e evite o layout thrashing agrupando as leituras e escritas no DOM.
Caching
O caching transforma visitas repetidas em carregamentos instantâneos. Utilize fingerprints em seus assets com hashes de conteúdo e sirva-os com um max-age longo Cache-Control, já que um arquivo alterado receberá um novo nome. Sirva o HTML com um cache curto e revalide-o. Para velocidade em visitas repetidas e funcionamento offline, um service worker pode fazer o cache do app shell e dos assets, conforme abordado no guia de PWA.
Orçamentos de performance
Um orçamento transforma a performance em uma restrição, em vez de apenas um desejo.
{
"budgets": [
{ "path": "/*", "resourceSizes": [{ "resourceType": "script", "budget": 170 }] }
]
}
Escolha um limite realista para JavaScript, imagens e LCP, e então aplique-o no CI. Quando um pull request excede o orçamento, o build falha e o autor decide se a troca vale a pena. É assim que as equipes evitam que a performance se degrade com o tempo.
Melhores práticas
- Meça antes de otimizar e utilize tanto dados de campo (field data) quanto de laboratório (lab data).
- Reduza a quantidade de JavaScript enviada antes de fazer micro-otimizações em qualquer outra coisa.
- Utilize lazy-load em imagens abaixo da dobra (below-the-fold) e faça o split de rotas pesadas.
- Defina dimensões explícitas em mídias para proteger o CLS.
- Use
fetchpriority,defereasyncde forma deliberada. - Utilize fingerprint em assets e configure o cache para longa duração.
- Defina e aplique um bundle budget no CI.
Erros comuns
- Otimizar a coisa errada sem realizar medições.
- Enviar um bundle do cliente enorme para conteúdos que poderiam ser estáticos.
- Carregar todas as imagens e scripts de forma ansiosa (eager loading).
- Omitir as dimensões das imagens, causando layout shift.
- Bloquear a main thread com tarefas síncronas longas.
- Tratar a performance como um projeto pontual em vez de um orçamento contínuo.
Próximos passos
Performance é uma disciplina, não um checklist. Use o Vite para dividir e analisar seu bundle, reduza o peso do JavaScript com os padrões do guia de React, adicione cache offline com PWA e mantenha a main thread livre com um JavaScript sólido. Depois, meça seu próprio site com dados de campo e escolha a alteração que trará o maior impacto.