Orçamento de prefetch de imagens no Service Worker 2025 — Prioridades inteligentes mantendo o INP saudável

Publicado: 29 de set. de 2025 · Tempo de leitura: 5 min · Pela equipe editorial da Unified Image Tools

Equipes adicionam prefetch de imagens para ganhar LCP, mas o Service Worker pode consumir banda e piorar o INP. Em 2025 temos Priority Hints estável e sinais melhores do Network Information API, permitindo tratar o prefetch como um “orçamento” dinâmico. Este artigo mostra como antecipar hero ou galeria sem comprometer a experiência.

TL;DR

  • Quantifique o orçamento: budget = (downlink × 0,25) - ativo LCP atual; se ficar negativo, pare o prefetch.
  • Reordene no Service Worker: colete telemetria de Navigation Timing + INP e ajuste prioridades para a próxima visita.
  • Sincronize Priority Hints e fetchpriority: deixe low no HTML, mas promova para auto/high no Service Worker conforme as condições.
  • Re-tente com Background Sync: cancele em offline/banda baixa e reagende via periodicSync quando houver Wi-Fi.
  • Observabilidade: registre acertos e ΔLCP no performance-guardian para validar o orçamento.

Modelando o orçamento

MétricaComo obterFrequênciaObjetivo
downlinknavigator.connection.downlinkInício + mudança de redeEstimativa de banda
effectiveTypeNetwork Information APIToda execuçãoClassificar 3G/4G/5G
inpP75PerformanceObserver + RUMToda execuçãoAlertar degradação de INP
lcpCandidateSizeperformance.getEntriesByType('largest-contentful-paint')Quando LCP finalizaMedir o ativo LCP
prefetchSuccessRateLogs do Service WorkerDiárioAvaliar eficácia do prefetch

Use esses sinais para decidir se vale antecipar ativos agora.

// sw/budget.ts
export function calculateBudget({ downlink, lcpSize, concurrentLoads }: {
  downlink: number
  lcpSize: number
  concurrentLoads: number
}) {
  const capacity = downlink * 125000 // Mbps -> bytes/s
  const reserved = lcpSize + concurrentLoads * 150000
  return Math.max(0, capacity * 0.25 - reserved)
}

Construindo a fila de prefetch

Liste os candidatos em prefetch-manifest.json.

[
  {
    "id": "hero-day2",
    "url": "/images/event/day2@2x.avif",
    "priority": 0.9,
    "type": "image",
    "expectedSize": 320000
  },
  {
    "id": "gallery-mini",
    "url": "/images/gallery/thumbs.webp",
    "priority": 0.4,
    "type": "image",
    "expectedSize": 90000
  }
]

O Service Worker lê o manifest e só enfileira itens que cabem no orçamento.

// sw/prefetch.ts
import { calculateBudget } from './budget'
import manifest from '../prefetch-manifest.json'

self.addEventListener('message', event => {
  if (event.data?.type !== 'INIT_PREFETCH') return
  const state = event.data.state
  const budget = calculateBudget({
    downlink: state.downlink,
    lcpSize: state.lcpSize,
    concurrentLoads: state.concurrentLoads
  })
  const queue = manifest
    .filter(item => item.expectedSize <= budget)
    .sort((a, b) => b.priority - a.priority)
  prefetchQueue(queue)
})

async function prefetchQueue(queue) {
  for (const entry of queue) {
    const controller = new AbortController()
    const timeout = setTimeout(() => controller.abort(), 4000)
    try {
      await fetch(entry.url, {
        priority: entry.priority > 0.7 ? 'high' : 'low',
        signal: controller.signal
      })
      await caches.open('prefetch-v1').then(cache => cache.add(entry.url))
      logPrefetch(entry.id, true)
    } catch (error) {
      logPrefetch(entry.id, false, error)
    } finally {
      clearTimeout(timeout)
    }
  }
}

fetchpriority ainda não é universal; se o navegador ignorar, reescreva <link fetchpriority> no HTML.

Sincronizando Priority Hints com o HTML

// app/layout.tsx
export function PrefetchHints() {
  return (
    <>
      <link
        rel="preload"
        as="image"
        href="/images/event/day2@2x.avif"
        fetchPriority="low"
      />
      <script
        dangerouslySetInnerHTML={{
          __html: `navigator.serviceWorker?.controller?.postMessage({
            type: 'INIT_PREFETCH',
            state: {
              downlink: navigator.connection?.downlink || 1.5,
              lcpSize: window.__LCP_SIZE__ || 200000,
              concurrentLoads: window.__IN_FLIGHT__ || 0
            }
          });`
        }}
      />
    </>
  )
}

Estratégias de cancelamento para proteger o INP

Quando o INP piorar, cancele imediatamente e reduza a prioridade para sessões futuras.

// sw/inp-monitor.ts
const INP_THRESHOLD = 200

new PerformanceObserver(list => {
  for (const entry of list.getEntries()) {
    if (entry.duration > INP_THRESHOLD) {
      self.registration.active?.postMessage({ type: 'CANCEL_PREFETCH' })
      updatePriority(entry.eventType)
    }
  }
}).observe({ type: 'event', buffered: true })

Cada ocorrência reduz a prioridade em 0,1, fazendo com que páginas com interações pesadas limitem o prefetch automaticamente.

Background Sync e prefetch noturno

Prefetch em banda ruim causa travamentos. Use periodicSync para tentar novamente quando houver melhores condições.

// sw/background-sync.ts
self.addEventListener('sync', event => {
  if (event.tag !== 'prefetch-sync') return
  event.waitUntil(prefetchQueue(manifest))
})

async function scheduleSync() {
  const registration = await self.registration.periodicSync?.register('prefetch-sync', {
    minInterval: 6 * 60 * 60 * 1000
  })
  return registration
}

Monitoramento e analytics

Envie eventos para performance-guardian.

sendToAnalytics('prefetch', {
  budget,
  downlink,
  prefetchSuccessRate,
  deltaLCP,
  deltaINP
})

KPI sugeridos:

KPIMetaCondição de alerta
ΔLCP (prefetch vs sem prefetch)≈ -180 msPositivo por 3 dias → revisar
INP p75< 180 ms> 200 ms → parar imediatamente
Prefetch Success Rate> 85%< 70% → ajustar manifest
Uso de banda< 30%> 50% → pausar experimento

Checklist

  • [ ] prefetch-manifest.json revisado por pares.
  • [ ] Parâmetros de calculateBudget validados em teste A/B.
  • [ ] Prefetch cancela assim que o INP piora.
  • [ ] Background Sync roda apenas em Wi-Fi.
  • [ ] ΔLCP/ΔINP disponível no performance-guardian.
  • [ ] Taxa de acerto de cache CDN comparada antes/depois do rollout.

Resumo

Prefetch sem controle desperdiça banda e fere o INP. Com orçamento dinâmico, prioridades ajustáveis e Background Sync, você torna o prefetch condicional: LCP melhora enquanto o usuário continua com uma experiência suave. Combine APIs do navegador com automação de CI para construir um ciclo de controle e evoluir a estratégia de prefetch conforme o perfil do seu site.

Artigos relacionados

Compressão

Throttling de streaming consciente da perda 2025 — Controlando banda AVIF/HEIC com SLOs de qualidade

Guia prático para equilibrar limitação de banda e SLOs de qualidade ao entregar formatos de alta compressão como AVIF/HEIC. Inclui padrões de controle de streaming, monitoramento e estratégia de rollback.

Fluxo de trabalho

Automatizando a otimização de imagens com um pipeline WASM 2025 — Integrando esbuild e Lightning CSS

Padrões para automatizar a geração, validação e assinatura de derivados de imagem com uma cadeia de build habilitada para WASM. Mostra como unir esbuild, Lightning CSS e Squoosh CLI para obter um CI/CD reproduzível.

Compressão

Estratégia Definitiva de Compressão de Imagem 2025 — Guia Prático para Otimizar Velocidade Percebida Preservando Qualidade

Análise abrangente das estratégias mais recentes de compressão de imagem para Core Web Vitals e operações do mundo real, com presets específicos, código e fluxos de trabalho por caso de uso. Cobre seleção JPEG/PNG/WebP/AVIF, otimização de build/entrega e solução de problemas.

Web

Auditor de SLA para CDN 2025 — Monitoramento baseado em evidências para entrega de imagens

Arquitetura de auditoria que comprova o cumprimento de SLA de imagens em ambientes multi-CDN. Cobre estratégia de medição, coleta de evidências e relatórios prontos para negociação.

Web

Monitoramento prático de Core Web Vitals 2025 — Checklist SRE para projetos enterprise

Playbook com visão de SRE que ajuda equipes de produção web enterprise a operacionalizar Core Web Vitals, cobrindo design de SLO, coleta de dados e resposta a incidentes ponta a ponta.

Compressão

Observabilidade da entrega de imagens Edge 2025 — Guia de design SLO e operações para agências web

Explica o design de SLO, dashboards de medição e operação de alertas para monitorar a qualidade de entrega de imagens em CDNs Edge e navegadores, com exemplos em Next.js e GraphQL pensados para agências web.