Performance

Site lento por JavaScript: como identificar e reduzir o peso

Imagens e fontes carregam-se otimizam-se com plugins. JavaScript, não. JavaScript é o problema que sobra depois de tudo o resto resolvido — e em 2026 é o maior responsável por sites lentos em mobile, scores baixos no PageSpeed, e INP acima de 200ms.

Este guia ataca o problema: como descobrir o que está a pesar, como reduzir sem partir funcionalidade, e como evitar que volte a crescer.

Porque é que o JavaScript dói mais do que as imagens

Uma imagem pesada custa bandwidth — demora a descarregar. Mas depois de descarregada, não pesa mais.

JavaScript custa três coisas:

  1. Descarga — bytes a transferir.
  2. Parse + compile — o navegador tem de interpretar e converter em código executável (custa CPU).
  3. Execução — corre. Bloqueia a main thread. Atrasa interação.

Em telemóveis de gama média (Android 200-400€, que é o que a maioria dos clientes em Portugal usa), 500 KB de JavaScript pode demorar 3-5 segundos a parse+execute. A página visualmente "está lá" mas não responde a clicks. É exatamente isso que o Google mede em INP.

Diagnosticar: as 3 ferramentas que importam

PageSpeed Insights — o resumo

Vai a pagespeed.web.dev com o teu URL. Procura especificamente:

  • "Reduce unused JavaScript" — quanto código foste obrigado a descarregar e nunca foi executado.
  • "Eliminate render-blocking resources" — scripts no <head> sem defer/async.
  • "Minimize main-thread work" — quanto tempo a CPU passou só com JavaScript.
  • "Reduce JavaScript execution time" — o que executou e quanto demorou.

Se algum destes ultrapassa 1.5s em mobile, tens problema sério. Detalhe completo no guia do PageSpeed Insights.

Chrome DevTools → Coverage

Abre o teu site no Chrome. F12 → Cmd+Shift+P → "Show Coverage". Recarrega. Para cada ficheiro JS verás quanto código foi carregado vs quanto foi realmente executado. Razão típica em sites de PME: 70-90% de JS não usado.

Chrome DevTools → Performance

Grava 5 segundos de carregamento. Procura as "Long Tasks" (tarefas >50ms) na main thread. Cada uma é um momento em que o utilizador clicou e nada aconteceu. Identifica que script as causa.

Os 7 culpados mais comuns

Em centenas de auditorias a sites WordPress de PME portuguesa, os mesmos suspeitos voltam:

  1. Google Tag Manager mal configurado — carrega 30 tags em vez das 5 necessárias.
  2. Plugins de slider (Revolution Slider, Smart Slider) — 200-500 KB por slider, mesmo onde não há slider.
  3. Plugins de chat (Tawk, Tidio, Crisp) — carregam síncronos no head.
  4. Page builders (Elementor, WPBakery) — JS para animações que ninguém pediu.
  5. Form builders pesados (WPForms premium, Gravity Forms com 12 add-ons).
  6. Bibliotecas duplicadas — tema carrega jQuery 1.x, plugin carrega jQuery 3.x.
  7. Tracking scripts sem consentimento gated (Meta Pixel, Hotjar, Clarity) a competir por main thread.

A boa notícia: identificar cada um leva 5 minutos com Chrome DevTools. Cortar cada um vale 100-500ms.

Defer vs async vs nada

Por defeito, um <script src="..."> no HTML bloqueia o navegador — pára de renderizar até descarregar e executar. Os atributos resolvem isso:

<!-- Bloqueia. Evitar. -->
<script src="grande.js"></script>

<!-- Descarrega em paralelo, executa logo que descarregado (pode interromper) -->
<script src="analytics.js" async></script>

<!-- Descarrega em paralelo, executa só depois do HTML pronto -->
<script src="ui.js" defer></script>

Regras práticas:

  • defer para scripts da página (UI, formulários). Mantém ordem de execução.
  • async para scripts independentes que não dependem nem afetam o resto (analytics, pixels).
  • Sem atributo só para scripts realmente críticos acima da dobra (raríssimo).

Em WordPress, plugins como Autoptimize ou Async JavaScript automatizam isto. Em Next.js, a tag <Script> tem strategy="afterInteractive" ou "lazyOnload".

Tag Manager: o assassino silencioso

Google Tag Manager é prático, mas vira pesadelo rápido. Cada tag adicional é JavaScript de terceiros a competir por main thread.

Auditoria mínima a fazer uma vez por trimestre:

  1. Abre o GTM. Lista todas as tags ativas.
  2. Para cada uma, pergunta: ainda usamos esta? Há quanto tempo?
  3. Apaga as inativas/desconhecidas. A maioria das contas tem 40% de tags mortas.
  4. Verifica triggers — tags a disparar em "All Pages" quando deviam estar em páginas específicas.
  5. Move o GTM para gated por consentimento de cookies (RGPD + performance).

Resultado típico: GTM passa de 250 KB de overhead para 80 KB. INP cai 100ms.

Plugins WordPress: matar primeiro, fazer cocó depois

Métrica simples: cada plugin ativo custa, em média, 30-80 ms de TTFB e 20-150 KB de JS/CSS frontend. Multiplica por 25 plugins (típico) e tens 1-2 segundos de custo.

Tratamento:

  • Listar todos os plugins. Para cada um, classificar: essencial / útil / esquecido.
  • Desativar os esquecidos. Se não notas nada 7 dias, apaga.
  • Substituir os pesados por leves. Slider Revolution → Swiper.js direto (10x mais leve). Contact Form 7 com add-ons → WPForms Lite ou um custom.
  • Lazy load de scripts não-críticos com plugin como FlyingScripts.

Lazy load de JavaScript de terceiros

Scripts como chat widgets, mapas embedded, players de vídeo, podem esperar pela primeira interação:

// Pseudo-código: carrega o chat só depois do utilizador interagir
['scroll', 'mousemove', 'touchstart'].forEach(e =>
  document.addEventListener(e, loadChat, { once: true })
);

Plugins WordPress como FlyingScripts ou Perfmatters fazem isto sem código. Resultado: o LCP cai imediatamente, e o chat continua a aparecer quando o utilizador interage.

Code splitting (para sites custom)

Para Next.js, React, ou framework moderno, o code splitting acontece automaticamente — cada página carrega só o JS dessa rota. Mas há erros comuns:

  • Importar bibliotecas pesadas no _app.tsx (vai para todas as páginas).
  • Usar import em vez de dynamic import para módulos só usados em modais.
  • Não tree-shake bibliotecas (importar lodash inteiro em vez de lodash/get).

next/dynamic resolve estes em 5 minutos.

Métricas-alvo realistas

Para PME portuguesa típica em 2026:

MétricaBomAceitávelMau
Total JS (transfer)< 200 KB200-400 KB> 400 KB
Tempo execução JS (mobile)< 1s1-2s> 2s
Long tasks0-23-5> 5
INP (p75)< 200 ms200-500 ms> 500 ms

Sites WordPress com Elementor + 5 plugins de marketing tendem a ficar na coluna "mau" sem esforço. Sites custom bem feitos ficam no "bom" sem esforço.

O que NÃO fazer

  • Minificar não é otimização. Reduz ~20% de bytes mas não muda parse+execute. Faz-se mas não conta como solução.
  • Não acreditar em plugin de "cache" mágico. Cache HTML serve a página pronta — não reduz JS no browser do utilizador.
  • Não substituir GTM por "tags hardcoded no tema". Perdes controlo, ganhas marginalmente.
  • Não migrar para framework "rápido" sem identificar o problema. Se o problema é Elementor + 5 chat widgets, mudar para Next.js sem mudar o resto piora.

Service Workers — vale a pena?

Service Workers (a base das Progressive Web Apps) permitem cachear JS no browser para visitas repetidas. Para um site institucional, o ganho é marginal — utilizadores típicos visitam uma vez por mês.

Para lojas online com utilizadores que voltam várias vezes por semana, faz diferença: o JS principal já está em cache local na segunda visita, TTFB para "carregar a app" cai a 0ms.

Em WordPress, plugins como PWA for WP simplificam. Em Next.js, next-pwa cobre o caso. Para PME pequena, vai à frente das tuas necessidades. Para loja com 5.000 visitantes/mês, considera.

Métrica em campo vs em laboratório

Quando otimizas JS, há duas realidades:

  • Lab (Lighthouse, PageSpeed Insights) — simula CPU 4x slower e rede 3G. Conservador. Bom para detetar regressões em PR.
  • Field (CrUX, RUM próprio) — visitantes reais com hardware real. É o que o Google rankeia.

Diferenças típicas: Lab diz INP 350ms. Campo diz INP 220ms (utilizadores reais têm dispositivos médios variados, alguns em desktop). Não confundas: otimiza para Lab passar limites, mas valida em campo antes de declarar vitória.

O ciclo de auditoria recomendado

Para PME que quer manter performance ao longo do tempo, não basta otimizar uma vez. Recomendação:

  1. Mensal — verificar Search Console Core Web Vitals. Se grupo de páginas piorou, investigar.
  2. Trimestral — auditoria de plugins WordPress: o que se acrescentou? O que se pode tirar?
  3. Semestral — Tag Manager: lista de tags ativas, comparar com o que se está mesmo a usar.
  4. Anual — auditoria completa: PageSpeed em 5 páginas-chave, comparar com ano anterior.

Sem este ciclo, sites bem otimizados degradam-se em 12-18 meses. Plugins acumulam, tags adicionam-se, ninguém limpa.

Em resumo

JavaScript pesado é hoje a maior causa de sites lentos em mobile e de INP fora dos limites. O problema é cumulativo: GTM com tags mortas, plugins esquecidos, sliders gigantes, chats síncronos, bibliotecas duplicadas. Diagnostica com PageSpeed Insights + Chrome DevTools Coverage (vais ver 70-90% de código não usado). Ataca em camadas: defer/async no que sobra, lazy load em scripts de terceiros, auditoria trimestral do Tag Manager, e substituição de plugins pesados por alternativas leves. Alvo realista para PME: <200 KB de JS, <1s de execução em mobile, INP <200ms. Maioria dos sites lá chega com 4-8 horas de trabalho — não com refactor de meses.


No sitesfixe.pt entregamos sites com JS auditado por defeito — defer/async corretos, sem bibliotecas duplicadas, com tracking gated por consentimento. Se o teu site WordPress tem INP acima de 500ms e ninguém sabe porquê, fala connosco. Sites desde 1.500€.

Lê também:

Fontes

Precisas de um site ou loja online?

Agência digital portuguesa. Sites e lojas online rápidos, otimizados para o Google e feitos para resultado.

Pedir orçamento