Mengelola anggaran prefetch gambar di Service Worker 2025 — Prioritas pintar dan INP tetap sehat

Diterbitkan: 29 Sep 2025 · Waktu baca: 5 mnt · Redaksi Unified Image Tools

Banyak tim menambahkan prefetch gambar demi LCP, tetapi Service Worker justru menyedot bandwidth dan memperburuk INP. Tahun 2025, Priority Hints sudah stabil dan Network Information API memberi sinyal lebih akurat, sehingga prefetch bisa dikendalikan secara dinamis. Artikel ini memperlakukan target asset dan timing sebagai “anggaran” dan menjelaskan cara memuat hero ataupun galeri lebih awal tanpa mengorbankan pengalaman pengguna.

TL;DR

  • Kuantifikasi anggaran: hitung budget = (downlink × 0,25) - aset LCP yang sedang dimuat; hentikan prefetch bila hasilnya negatif.
  • Re-ranking di Service Worker: kumpulkan telemetri Navigation Timing + INP, lalu sesuaikan peringkat prefetch untuk kunjungan berikutnya.
  • Sinkronkan Priority Hints dan fetchpriority: timpa nilai HTML (low) dari Service Worker, ubah ke auto/high sesuai kondisi.
  • Coba ulang dengan Background Sync: batalkan saat offline/bandwidth rendah dan jadwalkan ulang via periodicSync pada malam hari.
  • Observabilitas: catat keberhasilan prefetch serta ΔLCP ke performance-guardian untuk mengecek apakah anggaran tepat.

Mendesain model anggaran

MetrikCara memperolehFrekuensi disarankanTujuan
downlinknavigator.connection.downlinkAwal sesi & perubahan jaringanEstimasi bandwidth
effectiveTypeNetwork Information APISetiap eksekusiKlasifikasi 3G/4G/5G
inpP75PerformanceObserver + RUMSetiap eksekusiPeringatan degradasi INP
lcpCandidateSizeperformance.getEntriesByType('largest-contentful-paint')Saat LCP finalMemahami ukuran aset LCP
prefetchSuccessRateLog Service WorkerHarianMenilai efektivitas prefetch

Prefetch bersifat situasional; gunakan sinyal ini untuk memutuskan apakah anggaran tersedia sekarang.

// 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)
}

Membangun antrean prefetch

Kelola kandidat di 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
  }
]

Service Worker memuat manifest ini dan hanya mengantre item yang masih muat dalam anggaran.

// 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 masih eksperimental tetapi sudah jalan di Chrome/Safari. Untuk browser tanpa opsi priority, buat fallback yang menulis ulang atribut <link fetchpriority>.

Menghubungkan Priority Hints dengan 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
            }
          });`
        }}
      />
    </>
  )
}

Strategi pembatalan demi menjaga INP

Saat INP memburuk, hentikan prefetch seketika dan turunkan prioritas untuk sesi berikutnya.

// 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 })

CANCEL_PREFETCH menghentikan antrean; mengurangi priority sebanyak 0,1 tiap kejadian membuat halaman dengan interaksi berat menahan prefetch secara alami.

Background Sync dan prefetch malam hari

Memaksa prefetch di koneksi buruk membuat UI macet. Gunakan periodicSync untuk mencoba lagi saat Wi-Fi atau jam sepi.

// 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
}

Monitoring dan analitik

Kirim event RUM ke performance-guardian untuk memantau dampak prefetch.

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

KPI utama:

KPITargetKondisi alert
ΔLCP (prefetch vs tanpa)≈ -180 msBernilai positif 3 hari berturut-turut
INP p75< 180 ms> 200 ms → hentikan segera
Prefetch Success Rate> 85%< 70% → revisi manifest
Penggunaan bandwidth< 30%> 50% → jeda eksperimen

Checklist

  • [ ] prefetch-manifest.json masuk review kode.
  • [ ] Parameter calculateBudget diuji A/B.
  • [ ] Prefetch langsung berhenti ketika INP turun kualitas.
  • [ ] Background Sync hanya mencoba ulang saat Wi-Fi.
  • [ ] performance-guardian memvisualisasikan ΔLCP/ΔINP.
  • [ ] Rasio hit cache CDN dibandingkan sebelum/sesudah peluncuran.

Ringkasan

Prefetch tanpa kendali dapat menguras bandwidth dan merusak INP. Dengan model anggaran, prioritas dinamis, dan Background Sync, kamu bisa membuat prefetch bersyarat: LCP naik, pengalaman tetap mulus. Gabungkan API browser dengan otomatisasi CI untuk membangun loop kontrol dan kembangkan strategi prefetch sesuai karakter situs.

Artikel terkait

Kompresi

Throttling streaming sadar-loss 2025 — Kendali bandwidth AVIF/HEIC dengan SLO kualitas

Panduan praktik untuk menyeimbangkan throttling bandwidth dan SLO kualitas ketika melayani format kompresi tinggi seperti AVIF/HEIC. Bahas pola kontrol streaming, pemantauan, dan strategi rollback.

Alur kerja

Mengotomatiskan optimasi gambar dengan pipeline WASM 2025 — Integrasi esbuild dan Lightning CSS

Pola untuk mengotomatiskan pembuatan, validasi, dan penandatanganan derivatif gambar dengan rantai build berbasis WASM. Menjelaskan cara menggabungkan esbuild, Lightning CSS, dan Squoosh CLI agar CI/CD tetap reproducible.

Kompresi

Strategi Kompresi Gambar Lengkap 2025 — Panduan Praktis Optimasi Kecepatan Persepsi sambil Mempertahankan Kualitas

Strategi kompresi gambar terdepan yang efektif untuk Core Web Vitals dan operasi nyata, dengan preset khusus per penggunaan, kode, dan alur kerja yang dijelaskan secara detail. Mencakup pembedaan JPEG/PNG/WebP/AVIF, optimisasi build/delivery, dan diagnosis troubleshooting.

Web

Auditor Layanan CDN 2025 — Memantau SLA gambar berbasis bukti

Arsitektur audit untuk membuktikan kepatuhan SLA gambar di lingkungan multi-CDN. Membahas strategi pengukuran, pengumpulan bukti, dan pelaporan siap negosiasi.

Web

Monitoring Core Web Vitals Praktis 2025 — Checklist SRE untuk proyek enterprise

Playbook bernuansa SRE yang membantu tim produksi web enterprise mengoperasionalkan Core Web Vitals, mencakup desain SLO, pengumpulan data, dan respons insiden secara menyeluruh.

Kompresi

Observabilitas pengiriman gambar Edge 2025 — Panduan desain SLO dan operasi untuk agen web

Mengulas desain SLO, dasbor pengukuran, dan operasi alert untuk memantau kualitas pengiriman gambar melalui Edge CDN dan browser, lengkap dengan contoh implementasi Next.js dan GraphQL bagi agen web.