Ir para o conteúdo
Acecore

Técnicas práticas para alcançar 99 pontos no PageSpeed Mobile com site Astro

by Gui
Índice
Técnicas práticas para alcançar 99 pontos no PageSpeed Mobile com site Astro

Introdução

O site oficial da Acecore é construído com Astro 6 + UnoCSS + Cloudflare Pages. Neste artigo, apresentamos as técnicas de otimização realizadas para alcançar Mobile 99 pontos e Desktop 100 pontos no PageSpeed Insights.

As pontuações finais alcançadas são:

MétricaMobileDesktop
Performance99100
Accessibility100100
Best Practices100100
SEO100100

Por que escolhemos Astro

O que um site corporativo precisa é “velocidade” e “SEO”. O Astro é especializado em geração de sites estáticos (SSG) e alcança zero JavaScript por padrão. Como não envia componentes de frameworks como React ou Vue para o cliente, a exibição inicial é extremamente rápida.

Para o framework CSS, adotamos UnoCSS. Tem a mesma abordagem utility-first do Tailwind CSS, mas extrai apenas as classes utilizadas durante o build, resultando em CSS de tamanho mínimo. A partir da v66, presetWind3() é recomendado, então faça a migração.


Estratégia de entrega CSS: Inline vs Arquivo externo

O que teve maior impacto na pontuação do PageSpeed foi a estratégia de entrega CSS.

Quando o CSS é pequeno (até ~20 KiB)

Configurando build.inlineStylesheets: 'always' no Astro, todo o CSS é embutido diretamente no HTML. Como não é necessário fazer requisição HTTP para arquivo CSS externo, o FCP (First Contentful Paint) melhora.

Até cerca de 20 KiB de CSS, esta abordagem é ideal.

Quando o CSS é grande (20 KiB+)

No entanto, ao usar fontes Web japonesas (@fontsource-variable/noto-sans-jp), a situação muda. Este pacote contém 124 declarações @font-face (aproximadamente 96.7 KiB), e o CSS total chega a cerca de 190 KiB.

Se 190 KiB de CSS forem todos embutidos inline em cada HTML, o HTML da página inicial incha para 225 KiB. Em slow 4G, apenas a transferência deste HTML leva aproximadamente 1 segundo.

Solução: Arquivo externo + cache immutable

Altere a configuração do Astro para build.inlineStylesheets: 'auto'. O Astro julga automaticamente com base no tamanho do CSS e entrega CSS grande como arquivo externo.

// astro.config.mjs
export default defineConfig({
  build: {
    inlineStylesheets: 'auto',
  },
})

Os arquivos CSS externos são gerados no diretório /_astro/, então adicione cache immutable nas configurações de cabeçalhos do Cloudflare Pages.

/_astro/*
  Cache-Control: public, max-age=31536000, immutable

Com esta mudança, o tamanho do HTML foi reduzido em 84~91% (exemplo: index.html de 225 KiB → 35 KiB), e a pontuação do PageSpeed subiu de 96 pontos → 99 pontos.


Otimização de fontes: Configuração correta de self-hosting

Evite o Google Fonts CDN

O Google Fonts CDN é conveniente, mas no teste mobile do PageSpeed Insights é fatal. Nos testes realizados, com Google Fonts CDN o FCP foi de 6.1 segundos e a pontuação caiu para 62 pontos.

Em slow 4G, ao conectar a um domínio externo, ocorre a cadeia de DNS lookup → conexão TCP → handshake TLS → download do CSS → download da fonte, atrasando significativamente a renderização.

Introdução do self-hosting

Basta instalar @fontsource-variable/noto-sans-jp e fazer import no arquivo de layout.

npm install @fontsource-variable/noto-sans-jp
// BaseLayout.astro
import '@fontsource-variable/noto-sans-jp'

Atenção: Inconsistência no nome da fonte

Aqui existe uma armadilha inesperada. O nome da fonte que @fontsource-variable/noto-sans-jp registra no @font-face é Noto Sans JP Variable. Porém muitas pessoas escrevem Noto Sans JP no CSS.

Se houver essa inconsistência, a fonte não é aplicada corretamente e o navegador continua usando a fonte de fallback. Mesmo carregando 96.7 KiB de dados de fonte, eles não são utilizados.

Especifique corretamente a família de fontes nas configurações do UnoCSS.

// uno.config.ts
theme: {
  fontFamily: {
    sans: "'Noto Sans JP Variable', 'Hiragino Kaku Gothic ProN', 'メイリオ', sans-serif",
  },
}

Se ocorrer erro de tipo TypeScript, adicione a declaração de módulo em src/env.d.ts.

declare module '@fontsource-variable/noto-sans-jp';

Otimização de imagens: wsrv.nl + srcset + sizes

Proxy wsrv.nl

Imagens externas são entregues via wsrv.nl. Apenas adicionando parâmetros na URL, o seguinte processamento é realizado automaticamente:

  • Conversão de formato: output=auto seleciona automaticamente AVIF / WebP conforme o suporte do navegador
  • Ajuste de qualidade: q=50 mantém qualidade suficiente enquanto reduz o tamanho do arquivo em cerca de 10%
  • Redimensionamento: Redimensiona para a largura especificada com parâmetro w=

Configuração de srcset e sizes

Configure srcset e sizes em todas as imagens para entregar o tamanho ideal conforme a largura da tela.

<img
  src="https://wsrv.nl/?url=...&w=800&output=auto&q=50"
  srcset="
    https://wsrv.nl/?url=...&w=480&output=auto&q=50 480w,
    https://wsrv.nl/?url=...&w=640&output=auto&q=50 640w,
    https://wsrv.nl/?url=...&w=960&output=auto&q=50 960w,
    https://wsrv.nl/?url=...&w=1280&output=auto&q=50 1280w,
    https://wsrv.nl/?url=...&w=1600&output=auto&q=50 1600w
  "
  sizes="(max-width: 768px) calc(100vw - 2rem), 800px"
  loading="lazy"
  decoding="async"
/>

Precisão do sizes

Se o atributo sizes for deixado como 100vw (largura total da tela), o navegador seleciona uma imagem maior do que o necessário. Especifique conforme o layout real, como calc(100vw - 2rem) ou (max-width: 768px) 100vw, 50vw.

Melhoria do LCP: preload

Para imagens que afetam o LCP (Largest Contentful Paint), configure <link rel="preload">. Adicione uma prop preloadImage ao componente de layout do Astro para especificar imagens de prioridade máxima, como a imagem hero da página inicial.

<link rel="preload" as="image" href="..." />

Prevenção de CLS (Layout Shift)

Especifique os atributos width e height em todas as imagens. O navegador reserva antecipadamente a área de exibição da imagem, prevenindo mudanças de layout (CLS) ao completar o carregamento.

Particularmente fáceis de esquecer são imagens de avatar (32×32, 48×48, 64×64px) e thumbnails do YouTube (480×360px).


Carregamento lazy de anúncios e analytics

AdSense

O script do Google AdSense tem cerca de 100 KiB e impacta significativamente a exibição inicial. Injete o script dinamicamente no momento do primeiro scroll do usuário.

window.addEventListener('scroll', () => {
  const script = document.createElement('script')
  script.src = 'https://pagead2.googlesyndication.com/...'
  script.async = true
  document.head.appendChild(script)
}, { once: true })

Com { once: true }, o event listener dispara apenas uma vez. Isso reduz a quantidade de JavaScript transferido no first view a quase zero.

GA4

O Google Analytics 4 também é injetado com atraso usando requestIdleCallback. O script é injetado quando o navegador está em estado idle, sem interferir nas operações do usuário.


Estratégia de cache

Configure a política de cache ideal para cada tipo de asset no arquivo _headers do Cloudflare Pages.

# Saída do build (nomes de arquivo com hash)
/_astro/*
  Cache-Control: public, max-age=31536000, immutable

# Índice de busca
/pagefind/*
  Cache-Control: public, max-age=604800, stale-while-revalidate=86400

# HTML
/*
  Cache-Control: public, max-age=0, must-revalidate
  • /_astro/* contém hash no nome do arquivo, então cache immutable de 1 ano é seguro
  • /pagefind/* tem cache de 1 semana + 1 dia de stale-while-revalidate
  • HTML sempre busca a versão mais recente

Checklist de otimização de performance

  1. A estratégia de entrega CSS é adequada?: Abaixo de 20 KiB use inline, acima disso use arquivo externo
  2. As fontes são self-hosted?: CDN externo é fatal em slow 4G
  3. O nome da fonte está correto?: Verifique o nome de registro do @fontsource-variable (*Variable)
  4. Todas as imagens têm srcset + sizes?: Prepare especialmente tamanhos menores para mobile
  5. O elemento LCP tem preload?: Imagem hero e imagens do first view
  6. As imagens têm width / height?: Prevenção de CLS
  7. AdSense / GA4 estão com carregamento lazy?: Reduza a zero a transferência de JS no first view
  8. Os cabeçalhos de cache estão configurados?: Cache immutable para acelerar o segundo acesso em diante

Conclusão

O princípio da otimização de performance se resume a “não enviar o que não é necessário”. CSS inline parece rápido à primeira vista, mas com 190 KiB é contraproducente. Self-hosting de fontes é essencial, mas existe a armadilha da inconsistência no nome da fonte.

Com base na arquitetura zero JS do Astro, minimizando a quantidade de transferência em cada área — CSS, fontes, imagens e scripts de anúncios — alcançar 99 pontos no mobile é perfeitamente viável.


Série que inclui este artigo

Este artigo faz parte da série “Guia de melhoria de qualidade do site Astro”. Melhorias de SEO, acessibilidade e UX também são apresentadas em artigos individuais.

Fluxo de otimização

Estratégia de entrega CSS

Entender o trade-off entre inline e arquivo externo.

Otimização de fontes

Eliminar latência de CDN externo com self-hosting.

Otimização de imagens

Entregar tamanho ideal com wsrv.nl + srcset + sizes.

Carregamento lazy

Injetar AdSense e GA4 na primeira interação.

Comparação antes e depois da otimização

Antes da otimização
  • Google Fonts CDN (bloqueio de renderização)
  • 190 KiB de CSS inline no HTML
  • Imagens entregues em tamanho fixo
  • Script do AdSense carregado imediatamente
  • Mobile na faixa dos 70 pontos

Depois da otimização
  • Self-hosting com @fontsource (referenciando o nome correto da fonte)
  • CSS em arquivo externo com cache immutable
  • Tamanho ideal entregue conforme a largura da tela com srcset + sizes
  • Carregamento lazy de AdSense e GA4 ao primeiro scroll
  • Mobile 99 pontos, Desktop 100 pontos
Perguntas frequentes
CSS inline ou arquivo externo, qual é mais rápido?
Depende do tamanho total do CSS. Abaixo de 20 KiB, inline é vantajoso. Acima disso, arquivo externo aproveitando o cache do navegador é muito mais rápido a partir do segundo acesso.
Por que o Google Fonts CDN é lento?
O PageSpeed Insights simula slow 4G (aprox. 1.6 Mbps, RTT 150ms). Conectar a um domínio externo requer DNS lookup + conexão TCP + handshake TLS, e essa latência causa bloqueio de renderização. Com self-hosting, é entregue do mesmo domínio, eliminando essa latência.
O que fazer se o wsrv.nl estiver lento?
O wsrv.nl é entregue via CDN do Cloudflare, então normalmente é rápido. No entanto, se o cache do CDN não acertar durante a medição do PageSpeed, o LCP pode piorar. Para imagens importantes, configure <link rel="preload"> para instruir o navegador a buscar antecipadamente.
Carregar AdSense com lazy loading afeta a receita?
Quando não há anúncios no first view, o carregamento no primeiro scroll tem timing de exibição praticamente igual. O efeito SEO da melhoria na velocidade da página tende a ser mais positivo.
G

Gui

CEO da Acecore. Engenheiro versátil que trabalha com desenvolvimento de sistemas, produção web, operações de infraestrutura e educação em TI. Gosta de resolver desafios organizacionais e humanos por meio da tecnologia.

Desenvolvimento de sistemas Produção web Operações de infraestrutura Educação em TI

Quer saber mais sobre nossos serviços?

Oferecemos suporte abrangente em desenvolvimento de sistemas, design web, design gráfico e educação em TI.

Artigos relacionados

Pesquisar artigos