Uma landing page pode abrir rápido e ainda travar quando alguém toca no menu, digita no formulário ou aceita o banner de consentimento. O HTML já apareceu, mas a thread principal está ocupada demais para responder.
Isso costuma acontecer quando o código gerado por IA acumula bibliotecas, listeners, animações e scripts externos sem medir o custo de execução. Baixar menos bytes ajuda, mas não basta. O navegador ainda precisa analisar, compilar e executar o JavaScript que chegou.
O conserto começa com uma interação reproduzível. Grave a execução, encontre a tarefa que bloqueou a próxima pintura e reduza o trabalho naquele caminho. Só depois faz sentido republicar e medir a mesma URL.
O que roda na thread principal
A thread principal cuida de boa parte do trabalho que transforma os arquivos em uma página interativa:
- analisa HTML e CSS;
- calcula estilos e layout;
- executa JavaScript;
- processa eventos de clique, toque e teclado;
- prepara pintura e renderização.
Ela processa uma tarefa por vez. Segundo o guia do web.dev sobre long tasks, o navegador considera longa uma tarefa que ultrapassa 50 milissegundos. O trecho acima desse limite é o período de bloqueio.
O número não vira uma ordem para dividir qualquer função de 51 ms. Ele ajuda a localizar blocos que impedem o navegador de atender trabalho mais urgente, principalmente interações e pintura.
Separe transferência de execução
Dois scripts com o mesmo tamanho podem ter custos bem diferentes. Um apenas registra um componente pequeno. O outro analisa uma árvore grande, mede elementos, cria listeners e dispara um widget externo.
Revise quatro custos separadamente:
| Etapa | Pergunta prática | Evidência |
|---|---|---|
| transferência | quantos bytes chegaram pela rede? | Network, bytes transferidos e compressão |
| análise e compilação | quanto trabalho o navegador fez antes de executar? | trace de Performance |
| execução | quais funções ocuparam a thread principal? | Main track e Bottom-up |
| renderização | o callback forçou estilo, layout ou pintura? | eventos de rendering no trace |
O guia de compressão Brotli e gzip cobre transferência. Este artigo trata do trabalho que continua depois do download.
Comece pela interação ruim
Não abra o bundle e remova dependências ao acaso. Reproduza o atraso que afeta uma pessoa.
Exemplos úteis:
- abrir o menu no celular;
- focar e digitar no campo principal;
- enviar o formulário;
- expandir uma pergunta frequente;
- trocar uma opção de preço;
- aceitar ou recusar cookies;
- abrir um chat ou vídeo.
Registre a URL, a versão publicada, o dispositivo e a sequência de ações. Em seguida, grave um trace no painel Performance do Chrome DevTools com desaceleração de CPU compatível com um celular intermediário.
O painel mostra tarefas longas com um triângulo vermelho. Selecione a que cruza a interação e percorra a pilha de chamadas. A referência do painel Performance também permite separar trabalho próprio e de terceiros.
Anote antes de editar:
URL:versão publicada:dispositivo e desaceleração:interação reproduzida:duração total da interação:script e função dominantes:tarefa longa observada:trabalho próprio ou de terceiro:Leia INP como uma sequência
Interaction to Next Paint não mede apenas o tempo do listener. A interação tem três partes:
- atraso de entrada, enquanto a thread está ocupada antes do callback começar;
- duração de processamento dos callbacks;
- atraso de apresentação, até o navegador mostrar o próximo frame.
A orientação atual para otimizar INP usa 200 ms ou menos como faixa boa no 75º percentil, separada entre mobile e desktop. Uma única execução de laboratório ajuda a diagnosticar, mas não substitui dados de campo.
Esse recorte evita uma correção errada. Se o atraso acontece antes do callback, talvez outro script esteja bloqueando a thread. Se acontece durante o processamento, reduza o handler. Se a apresentação demora, investigue layout e pintura.
Remova trabalho antes de dividir tarefas
A melhor tarefa é a que não precisa rodar. Antes de aplicar técnicas de agendamento, pergunte se o código entrega algo visível ou necessário.
Procure:
- biblioteca inteira usada por um efeito simples;
- componente hidratado sem interação;
- validação repetida a cada tecla quando poderia ocorrer no blur ou envio;
- listener duplicado após cada renderização;
- cálculo de layout em itens fora da tela;
- analytics disparado várias vezes para o mesmo evento;
- widget carregado antes de intenção ou consentimento;
- código de experimento encerrado;
- polyfill para navegadores fora do suporte real do projeto.
O artigo sobre scripts de terceiros ajuda a decidir o que manter, adiar ou remover. Se o script também recebe dados externos, preserve as verificações do guia de segurança para scripts externos.
Carregue cada recurso no momento certo
Scripts não críticos não precisam disputar a primeira dobra.
Use a semântica que corresponde ao comportamento:
deferbaixa em paralelo e executa depois da análise do HTML, preservando a ordem entre scripts deferidos;asyncexecuta assim que o download termina, sem preservar ordem com outros scripts assíncronos;type="module"já tem comportamento deferido por padrão;import()permite buscar um módulo quando a funcionalidade for acionada.
Um exemplo para um editor aberto sob demanda:
const button = document.querySelector("[data-open-editor]");
button?.addEventListener("click", async () => { const { openEditor } = await import("./editor.js"); openEditor();});Não use importação dinâmica em conteúdo essencial só para melhorar uma auditoria. Menu, formulário e consentimento precisam continuar disponíveis no momento em que a interface promete.
O guia de priorização com preload e preconnect explica descoberta e prioridade de rede. Esses hints não reduzem o custo de execução do JavaScript.
Mantenha o callback curto
O handler deve produzir a resposta visual necessária e deixar trabalho secundário para depois.
button.addEventListener("click", () => { panel.hidden = false; button.setAttribute("aria-expanded", "true");
setTimeout(() => { recordPanelOpened(); prepareRecommendations(); }, 0);});Mover trabalho para outra tarefa cria uma oportunidade para o navegador pintar. Não torna o trabalho gratuito. Se a função secundária ainda consome centenas de milissegundos, remova custo, divida o processamento ou leve cálculo pesado e independente para um Web Worker.
Evite trocar uma tarefa longa por centenas de timers minúsculos. Cada agendamento tem custo e pode alterar a ordem de efeitos. Divida apenas nos limites em que a interface pode responder entre blocos.
Cuidado com layout forçado
JavaScript pode misturar leitura e escrita do layout de um jeito que obriga o navegador a recalcular estilos repetidamente.
for (const card of cards) { card.style.width = `${container.offsetWidth / 3}px`;}O loop lê uma medida e escreve um estilo a cada passagem. Leia primeiro, escreva depois:
const cardWidth = container.offsetWidth / 3;
for (const card of cards) { card.style.width = `${cardWidth}px`;}Quando CSS Grid ou Flexbox resolve a distribuição, prefira CSS. Menos JavaScript no layout reduz trabalho e elimina estados intermediários difíceis de testar.
Debounce não é solução universal
Debounce pode reduzir validações, filtros ou buscas disparadas a cada digitação:
let timer;
search.addEventListener("input", () => { clearTimeout(timer); timer = setTimeout(() => updateResults(search.value), 150);});Ele também adiciona atraso deliberado. Não aplique debounce em foco, abertura de menu, feedback de botão ou outra resposta que precisa parecer imediata. Para animações ligadas a scroll, considere requestAnimationFrame e confirme no trace que o callback cabe no frame.
Preserve acessibilidade e medição
Uma melhora de INP não justifica remover teclado, foco, validação ou mensagem de estado.
Depois da alteração, teste:
- mouse, toque e teclado;
- foco visível e ordem de tabulação;
- estado
aria-expandedou mensagem equivalente; - formulário com sucesso e erro;
- consentimento antes de analytics;
- evento de conversão uma única vez;
- navegação com JavaScript indisponível quando houver fallback prometido.
O checklist de QA para landing pages ajuda a evitar uma troca de performance por regressão funcional.
Valide e republique a mesma versão lógica
Rode build e testes do projeto. Compare o tamanho dos bundles, mas volte ao trace da interação. O objetivo é reduzir o bloqueio observado. Um arquivo menor, sozinho, não comprova isso.
Para publicar no Site no Ar:
- gere o pacote final limpo;
- confirme que
index.htmlaponta para os assets atuais; - aplique a versão pelas file tools e publique pelo contrato de Publicação;
- preserve o mesmo Site e o mesmo slug na republicação;
- abra a URL final;
- repita a interação e grave outro trace comparável.
Um Site com domínio próprio também precisa ser testado no hostname final. O guia de Domínios explica a relação entre o endereço padrão e o Custom Hostname.
Prompt para uma revisão focada
Analise o custo de JavaScript desta interação sem publicar ainda.
URL e versão: [URL e commit]Interação: [passos exatos]Dispositivo: [modelo ou desaceleração]Evidência: [INP, trace ou long task]
1. Identifique a tarefa que bloqueia a próxima pintura.2. Separe transferência, execução e renderização.3. Remova trabalho desnecessário antes de propor agendamento.4. Preserve comportamento, acessibilidade, consentimento e analytics.5. Faça uma mudança pequena e rode os checks do projeto.6. Mostre como repetir o trace antes de republicar.JavaScript menor é um bom efeito colateral. A meta é concreta. A pessoa toca, a página responde e o navegador consegue pintar sem esperar uma fila de trabalho que não precisava estar ali.
