- Fundamentos: O que é Performance e por que Importa
- Core Web Vitals: LCP, INP e CLS na Prática
- Entrega & Rendering: CDN, Lazy Loading e Estratégias de Render
- JavaScript: Seu Bundle Está Pesado Demais
- CSS: Critical Path e Layout Thrashing
- DevTools: Explorando a Performance no Navegador
- Monitoramento: Medindo o que Importa em Produção
Performance não é só o que acontece no browser — começa muito antes, na forma como os recursos chegam até o usuário. Como você entrega e renderiza seu conteúdo tem impacto direto no TTFB, no LCP e na experiência percebida.
Carregamento de Recursos
A ordem e forma como os recursos são carregados determina o waterfall de rede — quanto mais paralelo e antecipado, melhor.
Resource hints são dicas para o browser sobre o que buscar antecipadamente.
Ordem recomendada no head:
<!-- 1. preconnect para origens críticas -->
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- 2. preload para recursos críticos da rota atual -->
<link rel="preload" href="/hero.webp" as="image" fetchpriority="high">
<link rel="preload" href="/fonte.woff2" as="font" type="font/woff2" crossorigin>
<!-- 3. prefetch para a próxima navegação provável -->
<link rel="prefetch" href="/pagina-seguinte.html">HTTP/2 resolve o head-of-line blocking do HTTP/1.1 com multiplexing — múltiplas requisições em paralelo em uma única conexão TCP.
HTTP/3 usa UDP (via QUIC) em vez de TCP, sendo mais resiliente a packet loss — especialmente relevante em conexões móveis instáveis.
Serviços como Vercel, Netlify e Cloudflare Pages habilitam HTTP/2 e HTTP/3 automaticamente. Se você gerencia seu próprio servidor, configure Nginx ou Caddy para habilitá-los.
Brotli comprime ~15-20% melhor que gzip para assets de texto. Use Brotli para assets estáticos (compressão offline, sem custo de CPU em tempo real) e gzip como fallback.
# nginx.conf
brotli on;
brotli_static on;
brotli_comp_level 6;
brotli_types text/plain text/css application/javascript application/json;
gzip on;
gzip_static on;Minificação de HTML, CSS e JS é obrigatória. Bundlers modernos (Vite, Next.js) fazem isso automaticamente em produção.
- preload
Recursos necessários logo na página atual (fontes do hero, CSS crítico, LCP image). Usar com moderação — preloads desnecessários consomem bandwidth
- preconnect
Origens de terceiros críticas: CDN, APIs, serviços de fonte. Estabelece DNS + TCP + TLS antecipadamente
- prefetch
Recursos necessários na próxima navegação provável (ex: página de produto ao hoverar um card)
- dns-prefetch
Fallback de preconnect para domínios menos prioritários. Apenas resolve o DNS
CDN e Caching
Servidores distribuídos geograficamente. O usuário recebe o conteúdo do node mais próximo. Um server no Brasil responde em ~5ms para SP; sem CDN, o mesmo server responde em ~200ms para Tóquio.
Assets com hash no nome: max-age=31536000, immutable. Páginas HTML: stale-while-revalidate=86400. O stale-while-revalidate serve cache imediatamente e revalida em background — zero latência para o usuário.
# Assets com fingerprint — cache para sempre
Cache-Control: public, max-age=31536000, immutable
# Páginas HTML — serve cache, revalida em background
Cache-Control: public, max-age=0, stale-while-revalidate=86400
Estratégias de Rendering
A forma como você renderiza páginas determina o TTFB, o LCP e o custo de infraestrutura. Clique entre as estratégias para ver como cada uma impacta o waterfall de carregamento:
Comparador de estratégias de rendering
HTML vazio + bundle JS. O browser monta o DOM após executar o JS.
Valores simulados em conexão 4G — variam conforme servidor, CDN e tamanho do conteúdo
Static Site Generation — HTML gerado em build time. O servidor entrega HTML completo e pronto, servido direto da CDN.
Melhor para: blogs, documentação, landing pages — conteúdo que não muda frequentemente.
✅ TTFB mínimo, LCP excelente, custo mínimo de infra ❌ Rebuild necessário para atualizar conteúdo
Incremental Static Regeneration (Next.js) — páginas geradas estaticamente e revalidadas em background após um intervalo.
// App Router
export const revalidate = 60; // revalida a cada 60 segundosO melhor dos dois mundos: TTFB baixo (HTML estático) + conteúdo relativamente fresco sem rebuild completo.
Server Side Rendering — HTML gerado por requisição, com dados frescos do servidor.
Melhor para: conteúdo personalizado por usuário, dados em tempo real, páginas que precisam de SEO mas não podem ser geradas em build time.
✅ Conteúdo sempre atualizado, SEO completo ❌ TTFB mais alto, custo de infra maior
Em vez de aguardar todo o HTML ser gerado, o servidor envia chunks progressivos conforme ficam prontos.
export default function Page() {
return (
<>
<Header /> {/* enviado imediatamente */}
<Suspense fallback={<ProductSkeleton />}>
<ProductDetails /> {/* enviado quando dados chegarem */}
</Suspense>
</>
);
}O browser começa a renderizar o Header enquanto o servidor ainda busca dados. O usuário vê conteúdo imediatamente.
Client Side Rendering — HTML quase vazio + bundle JS. O browser monta o DOM no cliente.
Melhor para: apps altamente interativos onde o conteúdo depende do estado do usuário (dashboards, editors, tools).
✅ Infraestrutura simples (só arquivos estáticos) ❌ LCP alto, SEO precário sem SSR, hidratação custosa
Lazy Loading
Adia o download de imagens e iframes até que estejam próximos da viewport. Sem JavaScript, sem configuração. Nunca use na imagem LCP.
Para componentes React que só devem renderizar quando entram na tela. Evita trabalho desnecessário para conteúdo below the fold.
Listas com 100+ itens criam 100+ nós DOM. Virtualização mantém no DOM apenas os itens visíveis — essencial para listas longas.
Fontes
Controla o comportamento durante o carregamento da fonte:
- block — texto invisível até a fonte carregar (FOIT)
Bloqueia a leitura completamente enquanto a fonte baixa. Evite em quase todos os casos.
- swap — fallback imediato, troca quando carregar
Melhor para legibilidade imediata, mas pode causar CLS se as métricas de fonte diferirem muito.
- fallback — 100ms invisível, depois fallback; troca em 3s
Compromisso entre FOIT e FOUT. Se a fonte não carregar em 3s, o fallback permanece.
- optional — 100ms invisível; sem troca após isso
Melhor para CLS. O browser decide se usa a fonte baseado na velocidade da conexão. Recomendado para body text.
Para a fonte principal do corpo: optional. Para títulos críticos above the fold: swap com size-adjust no fallback.
Google Fonts: CDN global, mas exige conexão com fonts.googleapis.com e fonts.gstatic.com. Com preconnect o custo é minimizado.
Self-hosting: controle total, mesma CDN do site, sem requisições de terceiros. Recomendado para performance máxima.
<!-- Self-hosted com preload -->
<link rel="preload" href="/fonts/inter-var.woff2" as="font" type="font/woff2" crossorigin>Carregue apenas os glifos que você usa. Um subset de Latin básico pode ser 60-70% menor que a fonte completa.
Ferramentas: glyphhanger, pyftsubset, ou o Google Fonts com o parâmetro text=.
<!-- Google Fonts com subset específico -->
https://fonts.googleapis.com/css2?family=Inter&text=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789No próximo artigo, focamos no JavaScript — como analisar seu bundle, implementar code splitting e usar React.lazy + Suspense de forma eficaz.
As minhas notas
Selecione um trecho e use Destacar no menu (somente neste aparelho).