Skip to content

Tudo para WordPress, desenvolvimento web — e não só

⚡ Como adicionar defer e async para scripts do WordPress no function.php

⚡ 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 defer e async e 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âmetro strategy, 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_tag com 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

defer

Em paralelo com a análise

Após o DOM carregar totalmente

Na ordem do DOM

async

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:

1wp_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:

1wp_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:

1if ( 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:

1function 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}
7add_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.

1function 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}
15add_filter('script_loader_tag', 'add_defer_to_scripts', 10, 2);

Para async, apenas mudam o atributo e o nome do array:

1function 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}
15add_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
2function 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}
18add_action('wp_enqueue_scripts', 'enqueue_google_maps');

Mesmo resultado via script_loader_tag:

1function 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}
7add_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 defer e executar o script como async. Este comportamento está incorporado na especificação HTML, o async tem 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 defer por carregamento bloqueante padrão para esse script específico. Ou envolva o código do script em DOMContentLoaded, assim pode funcionar com defer sem 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 = true apenas move a tag <script> do <head> para o final do <body>. O script ainda bloqueia a renderização, apenas mais tarde. O defer carrega 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 filtro script_loader_tag funciona 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 defer se todos os scripts dependentes também estiverem marcados como defer. O problema é que os plugins do WordPress raramente gerem atributos para os seus scripts. Se colocar defer no 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 com defer para 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