O Lighthouse aponta Eliminate render-blocking resources e lista três arquivos .js do seu próprio tema. Não instale plugin de otimização só por causa disso: desde o WordPress 6.3 o wp_enqueue_script() aceita um quinto parâmetro em array com a estratégia de carregamento.
add_action( 'wp_enqueue_scripts', function () {
wp_enqueue_script(
'meutema-app',
get_stylesheet_directory_uri() . '/assets/app.js',
array(),
'1.4.0',
array(
'strategy' => 'defer',
'in_footer' => true,
)
);
} );Esse quinto argumento era o velho booleano $in_footer, e continua aceitando true por compatibilidade. Passando o array, o core imprime o atributo defer na tag e ainda percorre a árvore de dependências antes: se algum script que depende do seu for bloqueante, ele rebaixa a estratégia sozinho para não inverter a ordem de execução.
E o script do plugin de terceiro?
Quando o handle já foi registrado por outro código, você só acrescenta o dado, com prioridade alta o bastante para rodar depois do registro:
add_action( 'wp_enqueue_scripts', function () {
wp_script_add_data( 'handle-do-plugin', 'strategy', 'defer' );
wp_script_add_data( 'pixel-analytics', 'strategy', 'async' );
}, 99 );Guarde o async para script que não depende de ninguém e do qual ninguém depende, tipo pixel de analytics. Ele executa assim que baixa, fora de ordem; no resto use defer.
A regra que faz o defer sumir sem avisar
Handle com script inline anexado na posição after nunca é adiado. Está no filter_eligible_strategies(), dentro de wp-includes/class-wp-scripts.php: se o has_inline_script( $handle, 'after' ) der verdadeiro, o método devolve lista vazia e a tag volta bloqueante, sem nenhum aviso na tela. Como o wp_add_inline_script( 'handle', $js ) usa after por padrão, basta o plugin chamar isso para o seu defer virar pó. A saída é mover aquela configuração para a posição before ou entregá-la via wp_localize_script(), que grava na chave data e não conta como inline.
Se travou nisso e precisa de ajuda, me chama aqui.