
⚡ Como adicionar defer e async para scripts do WordPress no function.php
As páginas carregam lentamente, o Google PageSpeed Insights mostra avisos laranja e o cliente pergunta: «porque é que o site está lento?» Nove em cada dez vezes, a causa principal é o JavaScript a bloquear a renderização. O navegador encontra uma <script>, para de construir o DOM, carrega e executa o script e só depois continua. Num site moderno com uma dúzia de plugins, este atraso transforma-se em segundos.
O WordPress durante muito tempo não forneceu uma forma padrão de controlar o carregamento de scripts. Os developers dançavam com soluções alternativas: filtrar a script_loader_tag, corrigir a saída através de clean_url ou até escrever walkers personalizados para WP_Scripts. Mas com o lançamento do WordPress 6.3 a situação mudou radicalmente e agora temos uma forma limpa e suportada de adicionar defer ou async a qualquer script sem um único hack.
Abaixo estão dois métodos funcionais: a abordagem nativa moderna (WP 6.3+) e o comprovado filtro script_loader_tag (WP 4.1+). Ambos foram testados em projetos reais, ambos preservam a integridade da fila de dependências.
💡 Visão geral rápida:
- Compreenda a diferença entre
defereasynce quando usar cada um, isto determina se a funcionalidade se quebra após a otimização - Use o método nativo do WordPress 6.3+ via
wp_enqueue_script()com o parâmetrostrategy, a abordagem mais limpa que preserva a ordem de execução - Se o site correr uma versão inferior à 6.3, aplique o filtro
script_loader_tagcom um array de handles, isto funciona a partir do WordPress 4.1 - Para vários scripts, reúna os handles num array e faça um loop, um filtro para todos os scripts em vez de copiar e colar
O que são defer e async e quando usá-los
Quando um navegador encontra uma tag <script> normal, faz três coisas em sequência: para de analisar o HTML, carrega o script, executa-o. Só depois regressa ao HTML. Numa página com cinco scripts no <head>, isto significa que o utilizador vê um ecrã branco enquanto o último plugin de comentários carrega, mesmo que o artigo em si já pudesse ter sido renderizado há muito tempo.
Os atributos defer e async resolvem este problema, mas funcionam de forma diferente:
Atributo | Quando carrega | Quando executa | Ordem de execução |
|---|---|---|---|
(nenhum) | Bloqueia a análise imediatamente | Imediatamente após o carregamento | Na ordem do DOM |
| Em paralelo com a análise | Após o DOM carregar totalmente | Na ordem do DOM |
| Em paralelo com a análise | Imediatamente após o carregamento | Quem carregar primeiro |
Defer é o cavalo de batalha para a maioria dos cenários. O script carrega em paralelo com o HTML e executa apenas quando o DOM está totalmente construído. A ordem é preservada: o script A executará antes do script B, mesmo que o B tenha carregado mais rápido. Isto é crítico para o jQuery e tudo o que depende dele.
Async é uma ferramenta para scripts independentes. Analytics, anúncios, widgets de redes sociais: não precisam do DOM, não se importam com a ordem, só precisam de correr o mais rápido possível. Mas se colocar async num script que depende do jQuery, provavelmente obterá $ is not defined.
Regra simples: o script depende de outros scripts ou do DOM → defer. O script é completamente autónomo → async. Em caso de dúvida, comece sempre com defer.
Método 1: Abordagem nativa do WordPress 6.3+
Desde julho de 2023 que um novo mecanismo funciona no core do WordPress. As funções wp_register_script() e wp_enqueue_script() receberam um quinto parâmetro sobrecarregado $args, um array onde pode especificar a estratégia de carregamento. Sem filtros, sem magia de strings, sem risco de quebrar a ordem de dependências.
Sintaxe básica para defer:
1 wp_enqueue_script( 2 'my-js-handle', 3 get_template_directory_uri() . '/js/my-script.js', 4 array('jquery'), 5 '1.0.0', 6 array( 7 'strategy' => 'defer', 8 'in_footer' => true, 9 ) 10 );
Para async, a mesma mecânica:
1 wp_enqueue_script( 2 'google-analytics', 3 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX', 4 array(), 5 '1.0.0', 6 array( 7 'strategy' => 'async', 8 'in_footer' => false, 9 ) 10 );
A chave in_footer dentro do array funciona como o antigo parâmetro booleano: true coloca o script no footer, false no <head>. Para defer, normalmente define true (o script espera pelo DOM de qualquer forma, não faz sentido carregá-lo cedo), para async o que funcionar.
A principal vantagem do método nativo é que o próprio core verifica a árvore de dependências. Se o script A com defer depende do script B e o B está registado sem estratégia (bloqueante), o WordPress não vai quebrar o seu site: irá automaticamente rebaixar a estratégia do script A para bloqueante. Ao usar script_loader_tag, não tem esta proteção, o filtro simplesmente insere o atributo sem olhar para as dependências.
Importante: o array $args apareceu no WordPress 6.3. Se um tema ou plugin tiver de funcionar em versões inferiores, use o método 2 ou adicione uma verificação:
1 if ( version_compare( $GLOBALS['wp_version'], '6.3', '>=' ) ) { 2 // native method 3 } else { 4 // script_loader_tag filter 5 }
Método 2: Filtro script_loader_tag (WordPress 4.1+)
Se o site correr uma versão inferior à 6.3 ou precisar de manter compatibilidade retroativa, aplique o comprovado filtro script_loader_tag. Existe desde o WordPress 4.1 e ainda funciona na perfeição.
O filtro é acionado imediatamente antes de a tag <script> ser enviada para o HTML, recebe a string da tag pronta, o handle do script e o caminho do ficheiro, e pode substituir src por defer="defer" src ou async="async" src.
Script único com defer:
1 function add_defer_to_my_script($tag, $handle) { 2 if ( 'my-js-handle' !== $handle ) { 3 return $tag; 4 } 5 return str_replace( ' src', ' defer="defer" src', $tag ); 6 } 7 add_filter('script_loader_tag', 'add_defer_to_my_script', 10, 2);
O código vai no functions.php do tema ativo ou, mais corretamente, num plugin de snippets separado como Code Snippets ou WPCode. Se o colocar no functions.php de um tema filho, quando mudar de tema os scripts voltarão a ser bloqueantes e não notará imediatamente.
O handle do script é o primeiro parâmetro que passou a wp_register_script() ou wp_enqueue_script(). É o que aparece na condição if. Não adivinhe o handle, abra o código fonte do plugin ou tema e encontre a chamada wp_enqueue_script.
Defer e async para vários scripts
Adicionar um filtro por script é um caminho para um functions.php inchado e erros de copiar e colar. A solução certa: um array de handles e um filtro com um loop.
1 function add_defer_to_scripts($tag, $handle) { 2 $scripts_to_defer = array( 3 'my-js-handle', 4 'another-handle', 5 'third-party-lib', 6 ); 7 8 foreach ( $scripts_to_defer as $defer_script ) { 9 if ( $defer_script === $handle ) { 10 return str_replace( ' src', ' defer="defer" src', $tag ); 11 } 12 } 13 return $tag; 14 } 15 add_filter('script_loader_tag', 'add_defer_to_scripts', 10, 2);
Para async, apenas mudam o atributo e o nome do array:
1 function add_async_to_scripts($tag, $handle) { 2 $scripts_to_async = array( 3 'google-tag-manager', 4 'facebook-pixel', 5 'hotjar', 6 ); 7 8 foreach ( $scripts_to_async as $async_script ) { 9 if ( $async_script === $handle ) { 10 return str_replace( ' src', ' async="async" src', $tag ); 11 } 12 } 13 return $tag; 14 } 15 add_filter('script_loader_tag', 'add_async_to_scripts', 10, 2);
Ambos os filtros podem ser ligados simultaneamente, defer nos seus scripts, async em trackers de terceiros. Funcionam de forma independente e não entram em conflito.
Exemplo prático: API do Google Maps
O Google Maps é um candidato clássico para defer. O mapa está tipicamente no footer da página de contactos, o script puxa mais de 100 KB e o utilizador não precisa do mapa de imediato. Além disso, a própria API não depende de outros scripts da página, um caso ideal.
Ligar e adiar:
1 // theme's functions.php 2 function enqueue_google_maps() { 3 if ( ! is_page('contacts') ) { 4 return; 5 } 6 7 wp_enqueue_script( 8 'google-maps-api', 9 'https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY', 10 array(), 11 null, 12 array( 13 'strategy' => 'defer', 14 'in_footer' => true, 15 ) 16 ); 17 } 18 add_action('wp_enqueue_scripts', 'enqueue_google_maps');
Mesmo resultado via script_loader_tag:
1 function add_defer_to_google_maps($tag, $handle) { 2 if ( 'google-maps-api' !== $handle ) { 3 return $tag; 4 } 5 return str_replace( ' src', ' defer="defer" src', $tag ); 6 } 7 add_filter('script_loader_tag', 'add_defer_to_google_maps', 10, 2);
Depois de instalar qualquer uma das variantes, verifique definitivamente o mapa na página de contactos. Abra a consola do navegador (F12), certifique-se de que não há erros de JavaScript e de que o mapa foi renderizado corretamente. Se obtiver um erro como initMap is not a function, significa que o seu script de inicialização também precisa de ser marcado como defer e colocado estritamente após a ligação da API.
Como verificar se defer e async estão a funcionar
Após a implementação vem a verificação. Sem ela, não sabe se a otimização funcionou ou se ficou apenas como código morto.
Abra o código fonte da página (Ctrl+U) e encontre os seus scripts. A tag <script> deve ter os atributos:
1 <script defer="defer" src="/wp-content/themes/my-theme/js/my-script.js"></script>
Se não houver atributos, verifique se o handle no filtro corresponde ao handle real do script. Erro comum: em wp_enqueue_script o handle é my-plugin-frontend, mas no filtro é my_plugin_frontend. Hífen versus underscore e o filtro ignora silenciosamente o script.
Toque final, Google PageSpeed Insights ou Lighthouse no separador Auditorias das ferramentas de desenvolvimento. A secção «Eliminar recursos que bloqueiam a renderização» deve mostrar melhoria. O ganho específico depende do número e tamanho dos scripts, mas para um site WordPress típico com 5 a 7 plugins, uma redução de 40 a 60% no JavaScript bloqueante é um resultado alcançável.
⁉️🤔 Perguntas frequentes
Posso usar defer e async no mesmo script?
Não. Se especificar ambos os atributos simultaneamente, o navegador irá ignorar o
defere executar o script comoasync. Este comportamento está incorporado na especificação HTML, oasynctem sempre prioridade. Escolha um com base no facto de a ordem de execução ser importante.
O que fazer se, após adicionar defer, o script deixou de funcionar?
Muito provavelmente, o script espera que o DOM ainda não esteja construído e tenta manipular elementos que não existem no momento da execução. Substitua
deferpor carregamento bloqueante padrão para esse script específico. Ou envolva o código do script emDOMContentLoaded, assim pode funcionar comdefersem erros. A segunda opção é preferível: mantém a otimização e corrige a compatibilidade.
Qual é a diferença entre defer e mover o script para o footer via wp_enqueue_script com $in_footer = true?
$in_footer = trueapenas move a tag<script>do<head>para o final do<body>. O script ainda bloqueia a renderização, apenas mais tarde. Odefercarrega em paralelo com a análise do HTML e executa estritamente após o DOM ser construído. O uso combinado (in_footer => true+strategy => 'defer') dá o efeito máximo: o script no footer não atrasa a primeira renderização e o defer garante que também não bloqueará a renderização final.
Devo atualizar o WordPress para a 6.3 só pelo método nativo?
Se o site está na versão 6.2 ou anterior, atualizar vale a pena não apenas pelo
strategy. O WordPress 6.3 fechou dezenas de vulnerabilidades e trouxe melhorias de desempenho no core. Mas se uma atualização for impossível por algum motivo, o filtroscript_loader_tagfunciona de forma absolutamente fiável desde a versão 4.1, lançada em 2014. Não perde nada ao usá-lo.
E quanto ao jQuery, defer ou deixar como está?
O jQuery deve carregar com
deferse todos os scripts dependentes também estiverem marcados comodefer. O problema é que os plugins do WordPress raramente gerem atributos para os seus scripts. Se colocardeferno jQuery enquanto um plugin de formulário de contacto liga o seu script sem atributos, o navegador executará o plugin antes do jQuery e o formulário quebrará. Conselho prático: comece comdeferpara os scripts do seu próprio tema. Não mexa no jQuery até ter testado todos os plugins do site.
O que colocar num site de produção em 2026
Se o servidor correr WordPress 6.3 ou mais recente, apenas o método nativo. Código limpo, proteção contra conflitos de dependências, suporte do core. Comece com defer para todos os scripts do tema e plugins criticamente importantes; reserve async para analytics e widgets de terceiros.
Se a versão for inferior à 6.3, o filtro script_loader_tag com um array de handles. Funciona há uma década, não há nada para quebrar. A única coisa que não pode fazer é verificar automaticamente a árvore de dependências, por isso adicione scripts ao array um de cada vez e verifique o site após cada um.
E o mais importante: nenhum método substitui a auditoria aos próprios scripts. Se um plugin de galeria liga 15 ficheiros apenas para mostrar três imagens, nem defer nem async ajudarão radicalmente. A otimização do carregamento começa com a pergunta «este script é mesmo necessário» e só depois «como carregá-lo».
🔗 Documentação oficial do WordPress 6.3, Estratégias de Carregamento de Scripts



