O que você está tentando enxergar quando “abre” o DevTools
Quando a performance piora, quase sempre existe um “gargalo dominante” escondido em algum ponto do caminho entre: solicitar recursos, baixar bytes, processar/parsear, executar JavaScript, calcular estilos, fazer layout, pintar e compor na tela. Este capítulo foca em quatro ferramentas/visões que ajudam a localizar esse gargalo com precisão: leitura de waterfall (rede), Long Tasks (main thread), Coverage (código não usado) e um método prático de identificação de gargalos combinando essas evidências.
A ideia não é “otimizar tudo”, e sim responder perguntas objetivas: qual recurso está atrasando o carregamento? O que está bloqueando o thread principal? Quanto JavaScript/CSS está sendo baixado e não é usado? O gargalo é rede, CPU, renderização, ou uma mistura?
Leitura de waterfall: como interpretar a linha do tempo da rede
O waterfall (aba Network) é uma representação temporal de como cada requisição acontece. Ele permite identificar bloqueios, dependências e desperdícios (arquivos grandes, muitos requests, prioridades erradas). Para leitura eficiente, você precisa entender as fases comuns de uma requisição e como elas se relacionam com o carregamento da página.
Componentes do waterfall que importam
- Queueing/Stalled: tempo esperando antes de iniciar (limite de conexões, prioridades, cache, service worker, etc.). Muito “stalled” pode indicar excesso de requisições concorrentes, prioridades ruins ou contenção de conexão.
- DNS Lookup: resolução de domínio. Alto aqui pode indicar falta de preconnect/dns-prefetch para origens críticas ou problemas de rede.
- Initial connection / SSL: handshake TCP e TLS. Alto aqui sugere custo de conexão repetida (muitas origens) ou ausência de reuse/HTTP2/HTTP3 efetivo.
- Request sent: geralmente pequeno.
- Waiting (TTFB): tempo até o primeiro byte. Alto TTFB aponta gargalo no servidor, CDN, cache, ou rota dinâmica pesada. Também pode ser efeito de rede ruim, mas é um sinal forte para investigar backend/caching.
- Content Download: tempo baixando o corpo. Alto aqui indica payload grande, compressão ausente/ineficiente, ou banda limitada.
Além das fases, observe: tamanho transferido (transfer size), tamanho do recurso (resource size), prioridade (Priority), iniciador (Initiator) e cadeia de dependências (quem disparou quem).
O que procurar primeiro (heurísticas rápidas)
- Recursos grandes no topo do tempo: imagens, bundles JS, fontes e CSS que aparecem cedo e levam muito para baixar.
- Muitos requests pequenos: overhead de cabeçalhos, contenção e fila. Pode ser sinal de falta de bundling (com cuidado), ou de muitos ícones/fontes/fragmentos.
- Bloqueio por CSS: CSS é render-blocking por padrão. Se o CSS principal demora, a renderização inicial também atrasa. No waterfall, CSS crítico aparece cedo; se ele tem download lento ou TTFB alto, é um alvo.
- JS bloqueando parser: scripts sem defer/async podem bloquear o parsing. No waterfall, eles aparecem como requests que, ao completar, destravam novas requisições iniciadas pelo parser.
- Fontes atrasando texto: fontes web podem causar FOIT/FOUT dependendo da estratégia. No waterfall, fontes iniciadas tarde ou com conexão cara podem atrasar a renderização de texto.
- Redirecionamentos: cada redirect adiciona round-trips. No waterfall, você verá uma requisição 3xx seguida de outra; isso é custo puro.
Passo a passo prático: leitura guiada do Network waterfall
1) Prepare a captura
Continue em nosso aplicativo
Você poderá ouvir o audiobook com a tela desligada, ganhar gratuitamente o certificado deste curso e ainda ter acesso a outros 5.000 cursos online gratuitos.
ou continue lendo abaixo...Baixar o aplicativo
- Abra DevTools > Network.
- Marque Disable cache (apenas durante o DevTools aberto) para simular primeiro carregamento.
- Use um perfil de rede (por exemplo, Fast 3G/Slow 4G) e, se possível, também um perfil de CPU throttling em Performance para cruzar evidências depois.
- Recarregue a página com Ctrl+R (ou “Empty cache and hard reload”).
2) Identifique o documento principal
- Clique no request do document (HTML).
- Observe TTFB e tamanho. Se o TTFB é alto, antes de mexer em JS/CSS, investigue cache/CDN/servidor.
- Veja em Headers se há cache-control adequado e se a resposta é comprimida (content-encoding br/gzip).
3) Ordene mentalmente por “cadeia crítica”
- Filtre por CSS e veja quais estilosheets carregam cedo. CSS crítico lento é um gargalo típico.
- Filtre por JS e observe scripts iniciados pelo parser (Initiator “parser”) versus scripts carregados por outros scripts (Initiator “script”). Dependências em cascata (script A baixa e só então dispara B) criam “waterfall em escada”.
- Verifique Font e quando elas iniciam. Se fontes começam tarde, pode ser porque o CSS que as referencia chegou tarde.
4) Procure “escadas” e “buracos”
- Escada: recursos que só começam depois que outro termina. Isso indica dependência serial (ex.: HTML → JS → mais JS → dados). Serialização aumenta tempo total.
- Buraco: períodos com pouca atividade de rede, mas a página ainda não está pronta. Muitas vezes isso é CPU (main thread) ou espera por timers/JS.
5) Use as colunas e detalhes
- Ative colunas como Priority, Protocol, Domain, Remote Address.
- Se muitos recursos críticos estão em múltiplos domínios, o custo de conexão pode ser alto (DNS/TLS repetidos).
- Se o protocolo é HTTP/1.1 em uma origem com muitos recursos, pode haver head-of-line por limite de conexões.
6) Inspecione um request suspeito
- Em Timing, veja onde está o tempo: TTFB, download, stalled.
- Em Initiator, entenda por que ele foi solicitado (HTML, CSS, JS, preload, etc.).
- Em Response, verifique se há payload excessivo (por exemplo, JSON gigante).
Padrões comuns de gargalo detectáveis no waterfall
- TTFB alto no HTML: gargalo de servidor/cache. Sintoma: tudo começa tarde porque o HTML chega tarde.
- CSS grande e cedo: gargalo de renderização inicial. Sintoma: CSS é baixado cedo e demora; fontes e imagens referenciadas podem iniciar tarde.
- Bundle JS grande com prioridade alta: gargalo de download e, depois, de CPU (parse/compile/execute). Sintoma: download longo seguido de “buraco” (CPU) antes de novas ações.
- Muitas origens: gargalo de conexão. Sintoma: muitos requests com DNS/SSL relevantes, especialmente em mobile.
- Dependências em cascata: gargalo de serialização. Sintoma: requests iniciam tarde porque dependem de JS que depende de outro JS.
Long Tasks: identificando bloqueios no main thread
Long Tasks são tarefas no thread principal (main thread) que duram mais de 50 ms. Quando o main thread fica ocupado por muito tempo, ele não consegue responder a input, nem atualizar a UI com fluidez, nem processar eventos rapidamente. Long Tasks são um indicador direto de “CPU bound”: o problema não é baixar bytes, é processar/rodar código.
Você encontra Long Tasks principalmente na aba Performance do DevTools, e também pode correlacionar com a aba Performance monitor e com métricas de responsividade. Aqui o objetivo é: descobrir qual script e qual função está consumindo tempo, e se isso acontece no carregamento, após interação, ou continuamente.
O que compõe uma Long Task
- Scripting: execução de JavaScript (handlers, inicializações, frameworks, parsing de JSON, loops, etc.).
- Rendering: recalcular estilos e layout (reflow), frequentemente causado por leituras/escritas no DOM em sequência.
- Painting: pintura de pixels; pode ser pesado com sombras, blur, grandes áreas, canvas, etc.
- Garbage Collection: pausas do coletor de lixo, muitas vezes associadas a alocações excessivas.
Passo a passo prático: capturar e analisar Long Tasks no Performance
1) Grave um perfil
- Abra DevTools > Performance.
- Marque Screenshots (ajuda a correlacionar com mudanças visuais) e, se disponível, Web Vitals para anotações.
- Clique em Record e recarregue a página (ou execute o fluxo que está lento, como abrir um menu, filtrar uma lista, etc.).
- Pare a gravação após reproduzir o problema.
2) Encontre Long Tasks rapidamente
- Na trilha Main, procure blocos longos (amarelo para scripting, roxo para rendering, verde para painting). Long Tasks geralmente aparecem como blocos extensos sem “respiro”.
- Use o zoom (scroll/trackpad) para focar no intervalo crítico (por exemplo, logo após o carregamento ou após um clique).
3) Abra o detalhe da tarefa
- Clique no bloco longo na trilha Main.
- No painel inferior, vá em Bottom-Up para ver quais funções consumiram mais tempo total.
- Use Call Tree para entender a cadeia de chamadas (quem chamou quem).
- Use Event Log para ver eventos (click, input, timer, rAF) e o que rodou em seguida.
4) Correlacione com scripts e bundles
- Quando a função aponta para um arquivo minificado, use Source maps (se disponíveis) para mapear para o código original.
- Se não houver source map, ainda dá para identificar o arquivo e a linha aproximada; isso já ajuda a localizar o módulo responsável.
5) Classifique o tipo de gargalo
- Long Task de Scripting: inicialização pesada, hidratação, parse/compile de bundle grande, loops, serialização/desserialização, manipulação intensa do DOM.
- Long Task de Rendering: layout thrashing (ler layout e escrever estilo repetidamente), mudanças em muitas classes/estilos, tabelas grandes, listas enormes sem virtualização.
- Long Task de Painting: efeitos visuais caros, grandes repaints, canvas pesado, imagens enormes sendo redimensionadas no cliente.
Sinais típicos e como confirmar
- “Buraco” no Network mas Main ocupado: rede terminou, mas a página ainda “não responde”. Confirme no Performance: blocos longos de scripting/rendering.
- Interação com atraso: clique e nada acontece por um tempo. Confirme no Performance: evento de click seguido por Long Task antes do handler terminar.
- Jank ao rolar: scroll travando. Confirme: tarefas longas durante scroll, muitos recalculates de style/layout, listeners não-passive, trabalho pesado em rAF.
Coverage: encontrando JavaScript e CSS baixados e não utilizados
Coverage (aba Coverage no DevTools) estima quanto do JavaScript e CSS carregado foi efetivamente executado/usado durante um período de gravação. É uma ferramenta direta para identificar desperdício de bytes e de parse/compile/exec, especialmente em páginas com bundles grandes, bibliotecas genéricas, CSS de componentes não renderizados e rotas que compartilham um “mega bundle”.
Coverage não é um veredito absoluto: “não usado” pode significar “não usado nesse fluxo”. Ainda assim, é excelente para priorizar: se 70% de um bundle não é usado no carregamento inicial, há espaço para code splitting, lazy loading, remoção de dependências, ou entrega condicional.
Passo a passo prático: usando Coverage para medir desperdício
1) Abra e inicie a coleta
- DevTools > More tools > Coverage.
- Clique em Start instrumenting coverage and reload page (ícone de recarregar).
2) Reproduza o fluxo
- Após recarregar, interaja com a página conforme o cenário que você quer medir (apenas carregamento inicial, ou carregamento + abrir menu + navegar para uma seção, etc.).
- Quanto mais fiel ao uso real, melhor a leitura.
3) Interprete a tabela
- Observe as colunas de Bytes used e Bytes unused (ou percentuais).
- Ordene por Unused bytes para achar os maiores desperdícios.
- Diferencie JS e CSS: CSS não usado no carregamento inicial costuma ser enorme em apps com muitos componentes/rotas.
4) Abra o arquivo e veja o que está marcado como não usado
- Clique em um item da lista para abrir o arquivo com marcações (trechos usados vs não usados).
- Isso ajuda a reconhecer padrões: bibliotecas inteiras, polyfills desnecessários, CSS de páginas não visitadas, temas completos, ícones, etc.
Como transformar Coverage em ações
- Code splitting por rota/componente: se um bundle contém código de várias telas, separe para carregar sob demanda.
- Lazy load de recursos não críticos: módulos de analytics, widgets, editores ricos, mapas, chat, etc.
- Remover dependências: bibliotecas grandes usadas para uma função pequena (ex.: datas, manipulação de arrays) podem ser substituídas por alternativas menores.
- CSS modular/por rota: evitar entregar CSS de componentes não renderizados no primeiro paint.
- Tree-shaking e build: garantir que o build remove exports não usados e que você está consumindo módulos ESM quando possível.
Uma validação importante: após aplicar mudanças, rode Coverage novamente no mesmo fluxo para confirmar redução real de bytes não usados.
Método prático para identificação de gargalos (rede vs CPU vs renderização)
As ferramentas acima ficam mais poderosas quando usadas em conjunto. Um erro comum é olhar apenas para Network (e concluir “a rede está lenta”) ou apenas para Performance (e concluir “o JS é pesado”) sem cruzar sinais. A seguir está um roteiro de diagnóstico que ajuda a chegar em uma causa provável com evidência.
Passo a passo: triagem e confirmação
1) Comece pelo Network: existe um bloqueio óbvio?
- HTML com TTFB alto? Isso atrasa tudo. Priorize investigar servidor/CDN/cache antes de micro-otimizações no front.
- CSS/JS muito grandes e cedo? Marque os maiores e anote tamanho, tempo de download e se há múltiplas origens.
- Há redirecionamentos, muitas conexões TLS, ou muitos recursos em cascata? Anote.
2) Vá para Performance: o main thread está saturado?
- Se o waterfall mostra que a maior parte dos recursos já baixou, mas a UI demora a ficar utilizável, procure Long Tasks.
- Se houver Long Tasks logo após o download de um bundle grande, é um padrão clássico: custo de parse/compile/exec e inicialização.
- Se Long Tasks aparecem após interações, o gargalo é no handler (scripting) ou em render/layout disparado por ele.
3) Use Coverage para quantificar desperdício e guiar cortes
- Se o bundle principal tem muito “unused”, há oportunidade de reduzir bytes e também reduzir tempo de parse/exec.
- Se o CSS tem grande parte não usada no fluxo inicial, considere separar CSS crítico do restante.
4) Volte ao Network e confirme a cadeia crítica após mudanças
- Depois de reduzir bundles ou dividir código, o waterfall deve mostrar menos bytes cedo e menos dependências em cascata.
- Se você adicionou lazy loading, confirme que os novos requests acontecem no momento certo (após interação, ou após o conteúdo principal estar visível).
Exemplo prático de investigação (cenário típico)
Sintoma: a página “carrega”, mas fica travada por 2–3 segundos antes de responder a cliques.
Investigação
- No Network, você vê um bundle
app.bundle.jsde 1.5 MB transferidos cedo, com download razoável. - No Performance, logo após o término do download, há uma Long Task de scripting de ~1200 ms, seguida por várias tarefas menores e um pico de GC.
- No Coverage, apenas 35% do JS do bundle foi usado no fluxo inicial.
Leitura do gargalo: o gargalo dominante é CPU no main thread, amplificado por excesso de JS entregue no carregamento inicial. Ações típicas: dividir por rota, lazy load de módulos não críticos, reduzir dependências, e revisar inicializações que rodam no startup.
Checklist de gargalos e evidências
- Gargalo de servidor: TTFB alto no HTML e/ou APIs críticas; no Performance, pouco trabalho antes do HTML chegar.
- Gargalo de rede/payload: Content Download alto em recursos grandes; muitos bytes transferidos; Coverage pode mostrar desperdício, mas o sintoma principal é download lento.
- Gargalo de CPU (JS): Long Tasks de scripting; tempo alto em parse/compile/execute; Coverage com muito JS não usado; “buracos” no Network com Main ocupado.
- Gargalo de render/layout: Long Tasks com rendering; muitos recalculates; mudanças de DOM em massa; jank em scroll.
- Gargalo de pintura/composição: tarefas de painting longas; repaints grandes; efeitos visuais caros; camadas excessivas.
Dicas operacionais para tornar o diagnóstico repetível
Compare sempre o mesmo fluxo
Coverage e Performance dependem do que você faz durante a gravação. Defina um roteiro curto (ex.: abrir página, esperar 5s, clicar no botão X, abrir modal Y) e repita após cada mudança.
Use “Initiator” para quebrar dependências em cascata
Quando um request começa tarde, o Initiator frequentemente revela o motivo: um script que só roda depois de outro, um CSS que referencia uma fonte, ou uma chamada disparada por um módulo carregado tardiamente. Isso ajuda a atacar a causa (por exemplo, remover uma dependência serial) em vez de apenas “otimizar o arquivo”.
Não confunda “não usado” com “inútil”
Coverage marca como não usado tudo que não foi executado/selecionado no período. Código de fallback, rotas não visitadas e estilos de estados não acionados podem aparecer como não usados. A utilidade está em descobrir o que não precisa estar no carregamento inicial, não necessariamente em deletar tudo.
Procure por padrões de “trabalho repetido”
Em Performance, tarefas repetidas de recalcular estilo/layout podem indicar que o código está alternando leituras e escritas de layout (por exemplo, ler offsetHeight e depois escrever estilos em loop). Mesmo sem entrar em correções específicas aqui, identificar esse padrão já direciona a investigação para “rendering” e não “rede”.
Valide com antes/depois
Ao aplicar uma mudança, valide em três níveis: (1) Network: menos bytes e menos dependências críticas; (2) Performance: menos Long Tasks e menor tempo total de scripting/rendering; (3) Coverage: redução de unused bytes no fluxo medido. Se apenas um melhora e os outros não mudam, você pode ter otimizado um aspecto que não era o gargalo dominante.