Skip to main content
BlogWeb Dev

Next.js Cache Components dan Direktif use cache

Bagaimana Cache Components membuat Next.js dinamis secara default dan memberi Anda caching eksplisit lewat direktif use cache, profil cacheLife, serta invalidasi cacheTag.

Matthews Wong

Software Engineer

Agustus 20269 menit baca
Next.js Cache Components dan Direktif use cache

Foto oleh Egor Litvinov on Unsplash

Ringkasan Utama

Cache Components adalah model Next.js 16 di mana data bersifat dinamis secara default dan Anda memilih untuk melakukan caching lewat direktif use cache. cacheLife menentukan berapa lama sebuah entri tetap segar, cacheTag memberinya label, dan revalidateTag menghapusnya sesuai kebutuhan. Model ini juga menjadikan Partial Prerendering sebagai perilaku render default.

Selama bertahun-tahun saya tidak pernah bisa menjawab satu pertanyaan sederhana dengan yakin: apakah halaman ini di-cache? Di App Router, sebuah fetch bisa di-cache secara default, sebuah segmen rute bisa statis, dan satu API dinamis liar bisa diam-diam membuat semuanya menjadi dinamis. Aturannya hidup di default framework, bukan di kode saya, jadi jawaban jujurnya biasanya angkat bahu lalu mengecek langsung di produksi.

Cache Components, model yang aktif lewat satu flag di Next.js 16, memperbaiki hal itu dengan membalik default-nya. Tidak ada yang di-cache kecuali saya menyatakannya. Saya cukup memakai satu direktif, use cache, untuk menandai persis rute, komponen, atau fungsi yang keluarannya ingin saya simpan, dan sisanya tetap dinamis serta di-stream saat permintaan datang. Tulisan ini membahas seluruh permukaannya: direktifnya, profil cacheLife, invalidasi cacheTag dan revalidateTag, flag konfigurasi, dan bagaimana semuanya menyatu ke Partial Prerendering.

Dari default implisit ke opt-in eksplisit

Cerita caching lama bersifat implisit. Permintaan fetch di-cache kecuali Anda memberi opsi no-store, rute penuh diprarender kecuali sesuatu memaksanya dinamis, dan helper seperti unstable_cache menambahkan lapisan ekstra. Hasilnya kuat tetapi sulit dinalar, karena keputusan tentang apa yang di-cache tersebar di antara default, opsi fetch, dan konfigurasi segmen rute, bukan tampak jelas di dalam komponen.

Cache Components membuat keputusan itu eksplisit dan lokal. Pengambilan data bersifat dinamis secara default, dan Anda memilih apa yang di-cache di level halaman, komponen, atau fungsi. Ketika Anda lupa melakukan caching pada sesuatu yang mahal, Anda tetap mendapat keluaran yang benar dan segar, bukan halaman yang diam-diam basi. Konsekuensi yang Anda terima adalah kini Anda menuliskan caching dengan sengaja, dan justru itulah intinya. Berikut perbandingan kedua model tersebut.

AspekCaching implisit (default lama)use cache eksplisit
Perilaku defaultData di-cache kecuali Anda menonaktifkannyaData dinamis kecuali Anda mengaktifkannya
Mekanisme opt-inforce-cache, konfigurasi rute, unstable_cacheSatu direktif use cache dalam scope
Kendali masa berlakuEkspor revalidate dan opsi per-fetchProfil cacheLife, bernama atau inline
InvalidasirevalidatePath dan revalidateTagcacheTag plus revalidateTag atau updateTag
Di mana keputusan beradaDefault framework dan berkas konfigurasiTerlihat di dalam scope yang di-cache itu sendiri

Mengaktifkannya dengan flag cacheComponents

Semuanya dimulai dari satu flag. Mengaktifkan cacheComponents di konfigurasi Next.js membuka direktif use cache, fungsi cacheLife, dan cacheTag. Di balik layar, satu flag ini menggantikan tiga opsi eksperimental terpisah yang dulu dikonfigurasi sendiri-sendiri, menyatukan render dinamis, caching, dan prarender menjadi satu pengaturan koheren yang diperkenalkan di Next.js 16.

// next.config.ts
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  cacheComponents: true,
}

export default nextConfig

Setelah flag aktif, use cache bisa berada di tiga level. Di bagian atas berkas ia meng-cache setiap ekspor, dan semua ekspor itu harus berupa fungsi async. Di bagian atas komponen ia meng-cache keluaran render komponen tersebut. Di bagian atas fungsi async mana pun ia meng-cache nilai kembalian fungsi itu. Kunci cache dibangun dari build ID, hash lokasi dan tanda tangan fungsi, serta argumen yang dapat diserialisasi, sehingga input berbeda otomatis mendapat entri berbeda.

Alur caching dan revalidasi nyata

Direktif itu sendiri memakai default yang masuk akal: konten tetap segar di klien selama lima menit dan direvalidasi di server setiap lima belas menit. Sebagian besar waktu saya ingin lebih sengaja, jadi saya memasangkan use cache dengan cacheLife untuk menentukan masa berlaku dan cacheTag untuk memberi label pada entri. Di sini sebuah daftar produk di-cache dengan profil hours dan diberi tag agar bisa saya hapus nanti.

// app/products/page.tsx
import { cacheLife, cacheTag } from 'next/cache'

async function getProducts() {
  'use cache'
  cacheLife('hours')   // stale 5m, revalidate 1h, expire 1d
  cacheTag('products') // label this entry for on-demand purges

  const res = await fetch('https://api.example.com/products')
  return res.json()
}

export default async function ProductsPage() {
  const products = await getProducts()
  return <ProductGrid products={products} />
}

Caching hanya separuh pekerjaan; separuh lainnya adalah invalidasi. Ketika stok berubah, sebuah Server Action memanggil revalidateTag dengan label yang sama. Itu menandai entri bertag tersebut basi dan membangunnya ulang di latar belakang, sehingga pembaca tetap mendapat respons instan sementara salinan segar dibuat. Jika saya butuh pembacaan berikutnya langsung melihat perubahan, saya akan memakai updateTag, yang dibuat untuk skenario baca-tulisan-sendiri.

// app/actions.ts
'use server'
import { revalidateTag } from 'next/cache'

export async function refreshProducts() {
  await updateInventory()
  // Serve stale, then rebuild the 'products' entry in the background
  revalidateTag('products')
}

Selalu tetapkan cacheLife eksplisit pada scope yang di-cache. Tanpa itu ia mewarisi revalidate default lima belas menit, dan cache bermasa-hidup pendek yang tersarang bisa diam-diam memperpendek seluruh pohon. Menamai profil membuat siapa pun bisa membaca scope dan tahu kesegarannya sekilas, tanpa menelusuri cache bersarang.

Profil cacheLife: stale, revalidate, expire

Setiap masa berlaku cache adalah tiga angka. Stale adalah berapa lama router klien menyajikan entri tanpa mengecek server. Revalidate adalah seberapa sering server membangunnya ulang di latar belakang, mirip Incremental Static Regeneration. Expire adalah batas keras: setelah selama itu tanpa lalu lintas, permintaan berikutnya menunggu salinan segar. Next.js menyediakan profil bawaan sehingga Anda jarang menyentuh angka mentahnya.

  • seconds: stale 30 detik, revalidate 1 detik, expire 1 menit, untuk data real-time seperti skor langsung.
  • minutes: stale 5 menit, revalidate 1 menit, expire 1 jam, untuk feed dan berita yang sering diperbarui.
  • hours: stale 5 menit, revalidate 1 jam, expire 1 hari, untuk stok atau cuaca yang bergeser sepanjang hari.
  • days: stale 5 menit, revalidate 1 hari, expire 1 minggu, untuk artikel dan pos blog.
  • max: stale 5 menit, revalidate 30 hari, expire 1 tahun, untuk halaman legal dan konten arsip yang nyaris tak berubah.

Anda juga bisa mendefinisikan profil bernama kustom di konfigurasi, atau memberi objek inline untuk kebutuhan sekali pakai. Properti apa pun yang Anda hilangkan mewarisi dari profil default. Satu catatan penting: profil dengan revalidate nol atau expire di bawah lima menit, termasuk profil seconds, diperlakukan sebagai lubang dinamis dan dikecualikan dari shell yang diprarender alih-alih ditanam di dalamnya.

Invalidasi cacheTag dan revalidateTag

Kedaluwarsa berbasis waktu cocok untuk konten yang terjadwal, tetapi sebagian besar invalidasi digerakkan oleh peristiwa: sebuah produk disunting, sebuah pos diterbitkan, sebuah komentar masuk. cacheTag melekatkan satu atau lebih label string pada entri cache, dan Anda bisa menghasilkan label itu dari datanya sendiri, misalnya memberi tag entri dengan id record. Satu panggilan menerima hingga 128 tag, masing-masing hingga 256 karakter.

Dari sebuah Server Action atau Route Handler Anda lalu memanggil revalidateTag dengan sebuah label untuk menghapus setiap entri yang membawanya. revalidateTag adalah opsi sajikan-basi-sambil-revalidasi: pembaca tetap mendapat salinan lama sampai pembangunan ulang selesai. updateTag adalah saudara yang lebih ketat untuk saat pengguna yang sama yang membuat perubahan harus melihatnya pada pembacaan berikutnya. Keduanya memberi kendali presisi alih-alih menghapus seluruh cache.

Scope yang di-cache berjalan terisolasi dan tidak bisa memanggil cookies, headers, atau membaca searchParams secara langsung; baca semua itu di luar scope dan oper nilainya sebagai argumen. Selain itu, data dinamis yang tak di-cache dan tidak dibungkus dalam batas Suspense akan melempar error saat build, sehingga Anda dipaksa membatasi bagian dinamis dengan sengaja.

Bagaimana kaitannya dengan Partial Prerendering

Cache Components bukan hanya soal data; ia juga model render. Mengaktifkannya menjadikan Partial Prerendering sebagai default, sehingga flag ppr eksperimental lama dan konfigurasi ppr per-rute sudah dihapus. Next.js memprarender sebuah shell HTML statis dari segala sesuatu yang dapat di-cache, menyajikan shell itu seketika, dan men-stream lubang dinamis begitu ia selesai dihitung, semuanya dalam satu rute.

Itulah sebabnya direktif dan batas Suspense bekerja berpasangan. use cache menentukan apa yang masuk ke shell statis; batas Suspense menandai di mana lubang dinamis di-stream dengan sebuah fallback. Anda berhenti memilih antara halaman sepenuhnya statis atau sepenuhnya dinamis dan malah menggabungkan keduanya di pohon yang sama. Bagi saya kemenangan sesungguhnya adalah keterbacaan: batas cache kini hidup di dalam kode, jadi saya akhirnya bisa menjawab apa yang di-cache dengan membaca berkasnya.

FAQ

Pertanyaan Umum

Jawaban singkat untuk pertanyaan yang paling sering diajukan pembaca tentang topik ini.

Apa itu direktif use cache di Next.js?

Direktif use cache menandai sebuah rute, komponen, atau fungsi async sebagai dapat di-cache di Next.js. Direktif ini dibuka oleh flag cacheComponents dan bisa berada di level berkas, komponen, atau fungsi. Next.js membangun kunci cache dari build ID, hash lokasi dan tanda tangan fungsi, serta argumennya yang dapat diserialisasi.

Apa beda caching dengan Cache Components dibanding Next.js lama?

Next.js lama meng-cache data secara implisit lewat default framework, opsi fetch, dan konfigurasi segmen rute. Cache Components membalik hal itu: data bersifat dinamis secara default dan Anda memilih caching secara eksplisit dengan use cache. Keputusan caching kini hidup di dalam kode, bukan tersebar di antara berbagai default.

Apa itu profil cacheLife di Next.js?

Profil cacheLife menetapkan tiga waktu pada scope yang di-cache: stale (berapa lama klien menyajikannya tanpa mengecek server), revalidate (seberapa sering server membangunnya ulang di latar belakang), dan expire (batas keras). Next.js menyediakan preset seperti seconds, minutes, hours, days, weeks, dan max, dan Anda bisa membuat profil kustom atau inline.

Bagaimana cacheTag dan revalidateTag bekerja bersama?

cacheTag melekatkan satu atau lebih label string pada entri cache, menerima hingga 128 tag masing-masing 256 karakter. Dari Server Action atau Route Handler Anda memanggil revalidateTag dengan sebuah label untuk menghapus setiap entri yang membawanya, menyajikan konten basi sementara ia dibangun ulang. Untuk baca-tulisan-sendiri, updateTag membuat perubahan terlihat pada pembacaan berikutnya.

Bagaimana kaitan Cache Components dengan Partial Prerendering?

Mengaktifkan cacheComponents menjadikan Partial Prerendering sebagai default, sehingga flag ppr eksperimental lama dan konfigurasi per-rute dihapus. Next.js memprarender shell HTML statis dari konten yang dapat di-cache, menyajikannya seketika, dan men-stream lubang dinamis begitu selesai. use cache menentukan apa yang masuk ke shell, sementara batas Suspense menandai tempat konten dinamis di-stream.