Komponen cache di Next.js: Halaman lebih cepat dengan pra-render sebagian

Kinerja rendering dalam aplikasi web seringkali dibatasi oleh pekerjaan server yang tidak dapat diselesaikan secara instan. Pengambilan data, personalisasi, dan agregasi backend semuanya membutuhkan waktu. Di banyak aplikasi, pekerjaan ini memblokir seluruh halaman, meninggalkan pengguna dengan layar kosong hingga semuanya selesai.

Next.js secara tradisional menangani ini melalui model statis secara default. Rute bersifat statis atau dinamis, dan keputusan tersebut berlaku untuk seluruh halaman. Ini bekerja dengan baik untuk kasus-kasus sederhana, namun menjadi terbatas seiring berkembangnya aplikasi. Sebagian besar halaman nyata menggabungkan data yang dapat diprediksi dengan data khusus permintaan, namun halaman tersebut dipaksa ke dalam mode rendering tunggal.

Next.js 16 memperkenalkan Komponen Cache untuk mengatasi batasan ini. Komponen Cache beroperasi pada tingkat komponen dan dibangun di atas Pra-Rendering Parsial. Pra-Rendering Parsial memungkinkan shell halaman untuk segera dirender sementara bagian yang lebih lambat ditunda. Komponen Cache memperluas model ini dengan memberi Anda kendali atas bagian halaman mana yang dapat digunakan kembali tanpa memblokir bagian lainnya.

Dalam artikel ini, kita melihat bagaimana Komponen Cache masuk ke dalam pipeline rendering Next.js dan bagaimana komponen tersebut mengubah cara konten statis dan dinamis hidup berdampingan di halaman yang sama.

🚀 Mendaftar untuk buletin The Replay

Pemutaran Ulang adalah buletin mingguan untuk para pemimpin pengembang dan teknik.

Disampaikan seminggu sekali, ini adalah panduan pilihan Anda untuk percakapan paling penting seputar pengembangan frontend, alat AI yang sedang berkembang, dan keadaan perangkat lunak modern.

Sekilas tentang Pra-Rendering Parsial (PPR)

Halaman modern jarang hanya menangani satu jenis data. Navigasi dan tata letak sering kali dapat diprediksi, data katalog atau konten terkadang berubah, dan data personalisasi atau sesi harus dihitung pada setiap permintaan. Ketika semua pekerjaan ini ditangani dalam satu render pass, operasi paling lambat menentukan kapan sesuatu dapat ditampilkan.

Pra-Rendering Parsial menghindari hal ini dengan mengizinkan halaman dirender secara bertahap. Shell statis dikirim terlebih dahulu untuk membentuk struktur, sementara bagian dinamis dibungkus dalam batas Suspense dan dirender secara independen:

Pra-Rendering Sebagian
Pra-render sebagian

Saat bagian tersebut selesai dirender, bagian tersebut dialirkan ke klien tanpa memblokir halaman lainnya. Pengguna melihat kemajuan, bukan layar kosong.

Pra-Rendering Parsial meningkatkan cara pengiriman halaman, namun tidak menentukan seberapa sering pekerjaan server harus dijalankan atau apakah hasilnya dapat digunakan kembali. Keterbatasan itulah yang membuat Komponen Cache diperlukan.

Memperkenalkan komponen cache

Meskipun Pra-Rendering Parsial meningkatkan cara pengiriman halaman, hal ini tidak mengubah apa yang terjadi di server. Bagian dinamis tetap berjalan ketika ada permintaan masuk. Jika suatu bagian mengambil data yang sama pada setiap permintaan, pekerjaan itu diulangi bahkan ketika hasilnya sama.

Inilah celah yang ingin diatasi oleh Komponen Cache. Dengan Komponen Cache, keputusan caching dipindahkan ke tingkat komponen. Daripada memperlakukan seluruh rute sebagai statis atau dinamis, Anda menandai bagian tertentu dari UI sebagai dapat di-cache dan membiarkan sisanya dinamis. Hal ini memungkinkan untuk menggunakan kembali keluaran yang dirender oleh server yang dapat diprediksi tanpa memengaruhi bagian khusus permintaan:

Komponen Cache Beraksi
Komponen cache sedang beraksi

Diagram menunjukkan halaman yang sama sedang dirender pada dua permintaan terpisah:

Permintaan 1 mewakili pertama kali halaman dirender. Semua bagian dijalankan di server. Komponen ditandai C memenuhi syarat untuk cache, sehingga outputnya dihitung dan disimpan. Komponen ditandai D bersifat dinamis dan dijalankan sebagai bagian dari permintaan.

Permintaan 2 mewakili permintaan selanjutnya untuk halaman yang sama. Komponen yang di-cache tidak dapat dijalankan lagi. Output yang diberikan sebelumnya digunakan kembali. Komponen dinamis masih berjalan di server karena bergantung pada data khusus permintaan.



Tata letak halaman tidak berubah antar permintaan. Yang berubah adalah siklus hidup setiap bagian. Komponen Cache memungkinkan bagian halaman yang dapat diprediksi untuk digunakan kembali di seluruh permintaan, sementara bagian dinamis terus berjalan setiap saat.

Mengonfigurasi komponen cache

Untuk mendemonstrasikan Komponen Cache sepenuhnya, ada baiknya jika Anda terlebih dahulu melihat bagaimana perilaku halaman tanpa komponen tersebut. Untuk alasan ini, saya membangun sebuah aplikasi yang menggunakan cara tradisional. Anda dapat menemukan repositori GitHub untuk ini di sini.

Versi ini tidak menggunakan Komponen Cache dalam bentuk apapun. Semuanya terjadi dalam satu server render. Anda dapat mengonfirmasinya di next.config.ts:

// next.config.ts
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
  /* config options here */
};
export default nextConfig;

Pada halaman itu sendiri, rendering dinamis secara eksplisit dipaksakan. Hal ini memastikan bahwa setiap permintaan menjalankan logika server baru dan tidak ada yang digunakan kembali.

import ProductGrid from '@/components/product-grid';        // 2 seconds
import Recommendations from '@/components/recommendations'; // 3 seconds
import Orders from '@/components/orders';                   // 2 seconds

Anda akan melihat bahwa ada tiga sumber data independen:

  • Produk, yang memakan waktu sekitar dua detik
  • Rekomendasi, yang memakan waktu sekitar tiga detik
  • Pesanan terbaru, yang memakan waktu sekitar dua detik

Karena panggilan-panggilan ini ditunggu satu demi satu, total waktu tunggu server kira-kira tujuh detik. Lebih penting lagi, pernyataan return hanya berjalan setelah permintaan terakhir selesai. Sampai saat itu tiba, React tidak punya apa pun untuk dirender. Untuk mengonfirmasi hal ini, mulai aplikasi dengan menjalankan perintah:

npm run dev

Navigasikan ke Hal pertama yang Anda perhatikan adalah tidak adanya apa pun. Tidak ada tajuk. Tidak ada tata letak. Tidak ada status pemuatan. Peramban kosong:

Mengonfigurasi Komponen Cache

Saat halaman akhirnya dirender, halaman tersebut akan dirender dalam waktu sekitar tujuh detik. Penundaan tersebut terjadi karena halaman disusun untuk menunggu bagian data paling lambat sebelum merender apa pun. Dalam praktiknya, sebagian besar UI tidak perlu menunggu. Header, footer, dan bagian halaman lainnya yang tidak bergantung pada data lambat dapat segera dirender.

Di sinilah peran Pra-Rendering Parsial. Pra-Rendering Parsial memungkinkan halaman untuk merender shell statis terlebih dahulu, sementara bagian yang lebih lambat ditunda. Ini memungkinkan Anda mencampur jenis rendering pada halaman yang sama. Namun, ia mempunyai keterbatasan. Meskipun bagian yang ditangguhkan dapat dialirkan, bagian tersebut masih dihitung ulang pada setiap permintaan. Pra-Rendering Parsial meningkatkan pengiriman, namun tidak memungkinkan bagian dinamis tersebut di-cache. Keterbatasan itulah yang membawa kita ke Komponen Cache.

Sekarang mari kita konfigurasikan komponen cache.

Pertama, kita perlu mengonfigurasi komponen cache untuk mengaktifkannya, karena komponen tersebut masih dalam tahap percobaan. Membuka next.config.ts dan perbarui sebagai berikut:

import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
  experimental: {
    useCache: true,
  },
};
export default nextConfig;

Bendera ini memungkinkan use cache direktif dan memberitahu Next.js untuk mengizinkan caching di tingkat komponen.

Selanjutnya, kita memerlukan tempat untuk menentukan berapa lama data cache akan bertahan. Di aplikasi, setiap permintaan mengambil ulang semuanya. Kami ingin mengubahnya dengan Komponen Cache sehingga kami dapat memiliki kontrol eksplisit atas kesegaran.


Lebih banyak artikel bagus dari LogRocket:


Buat file baru dilib/cache-config.ts:

export const cacheProfiles = {
  products: {
    stale: 3600,      // 1 hour
    revalidate: 7200, // 2 hours
    expire: 86400,    // 24 hours
  },
};

Penting untuk diperhatikan bahwa file ini tidak mengubah perilaku dengan sendirinya. Itu hanya memusatkan kebijakan cache sehingga dapat digunakan kembali di seluruh komponen.

Jadikan grid produk dapat di-cache

Membuka components/product-grid.tsx dan ganti isinya dengan versi cache di bawah ini:

'use cache';
import { fetchProducts } from '@/lib/demo-data';
import { cacheProfiles } from '@/lib/cache-config';
export async function cacheLife() {
 return cacheProfiles.products;
}
export default async function ProductGrid() {
 const products = await fetchProducts();
 return (
   <section style={{ marginBottom: '3rem' }}>
     <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1.5rem' }}>
       <h2 style={{ fontSize: '1.5rem', fontWeight: 'bold' }}>Products</h2>
     </div>
     <div className="grid grid-4">
       {products.map(product => (
         <div key={product.id} className="card fade-in">
           <div style={{ fontSize: '3rem', marginBottom: '0.5rem' }}>{product.image}</div>
           <h3 style={{ fontSize: '1.125rem', fontWeight: '600', marginBottom: '0.25rem' }}>
             {product.name}
           </h3>
           <p style={{ color: '#888', fontSize: '0.875rem', marginBottom: '0.5rem' }}>
             {product.category}
           </p>
           <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
             <span style={{ fontSize: '1.25rem', fontWeight: 'bold', color: '#10b981' }}>
               ${product.price}
             </span>
             <span style={{ fontSize: '0.875rem', color: '#888' }}>
               Stock: {product.stock}
             </span>
           </div>
         </div>
       ))}
     </div>
   </section>
 );
}

Di bagian atas file adalah 'use cache'sinyal, yang memberi tahu Next.js bahwa komponen ini diizinkan untuk digunakan kembali alih-alih dihitung ulang pada setiap permintaan. Tanpanya, komponen akan berperilaku persis seperti sebelumnya dan berjalan setiap kali halaman dirender.

Kami juga menambahkan fungsi cacheLife(), yang mana kami memutuskan berapa lama grid produk harus bertahan. Karena produk tidak berubah pada setiap permintaan, masuk akal untuk menggunakannya kembali untuk jangka waktu tertentu daripada mengambilnya terus-menerus. Alih-alih menyimpan seluruh halaman dalam cache, pendekatan ini memungkinkan kita untuk berhati-hati dan hanya menyimpan cache pada bagian yang mendapat manfaat darinya.

Isolasi bagian yang lambat dengan Suspense

Setelah menyimpan grid produk dalam cache, tujuan selanjutnya adalah membiarkan halaman dirender sesegera mungkin, sementara hanya bagian yang bergantung pada data lambat yang diperbolehkan menunggu. Di situlah Suspense berperan. Ini memungkinkan kita menandai bagian halaman yang dapat dimuat nanti, jadi sementara bagian tersebut masih diselesaikan di server, React dapat mengirimkan sisa halaman ke browser dan mengisi kekosongan setelahnya.

Untuk membuat penundaan itu terlihat, kita memerlukan sesuatu untuk ditampilkan sebagai pengganti konten sebenarnya.

Buat file baru di components/loading-skeleton.tsx:

export default function LoadingSkeleton({ count = 4 }: { count?: number }) {
  return (
    <div className="grid grid-4">
      {Array.from({ length: count }).map((_, i) => (
        <div key={i} className="card skeleton" />
      ))}
    </div>
  );
}

Kode di atas menampilkan kotak kecil kartu yang kira-kira sesuai dengan bentuk ubin produk di UI, sehingga halaman tidak terasa kosong saat kita menunggu data sebenarnya tiba.

Sekarang buka app/page.tsxdan ganti isinya dengan yang berikut ini:

import './globals.css';
import { Suspense } from 'react';
import { getTimestamp } from '@/lib/demo-data';
import LoadingSkeleton from '@/components/loading-skeleton';
import ProductGrid from '@/components/product-grid';              
import Recommendations from '@/components/recommendations';       
import Orders from '@/components/orders';                         
export const dynamic="force-dynamic";
export default function CacheApp() {
  const timestamp = getTimestamp();
  return (
    <>
      <div className="container" style={{ padding: '2rem 1rem' }}>
        {/* Header Banner */}
        <div style={{ 
          marginBottom: '2rem', 
          padding: '1.5rem', 
          background: '#065f46', 
          borderRadius: '12px', 
          border: '2px solid #10b981' 
        }}>
          <h1 style={{ fontSize: '2rem', fontWeight: 'bold', marginBottom: '0.5rem' }}>
            Ecommerce App
          </h1>
          <p style={{ fontSize: '1rem', color: '#86efac', marginBottom: '0.5rem' }}>
            Cached content renders immediately, while dynamic sections load progressively.
          </p>
          <p style={{ fontSize: '0.875rem', color: '#d1fae5', fontFamily: 'monospace' }}>
            Rendered at: {timestamp}
          </p>
        </div>
        <Suspense fallback={
          <section style={{ marginBottom: '3rem' }}>
            <div style={{ marginBottom: '1.5rem' }}>
              <h2 style={{ fontSize: '1.5rem', fontWeight: 'bold' }}>Products</h2>
              <p style={{ color: '#888', fontSize: '0.875rem' }}>Loading products...</p>
            </div>
            <LoadingSkeleton count={4} />
          </section>
        }>
          <ProductGrid />
        </Suspense>
        <Suspense fallback={
          <section style={{ marginBottom: '3rem' }}>
            <div style={{ marginBottom: '1.5rem' }}>
              <h2 style={{ fontSize: '1.5rem', fontWeight: 'bold' }}>Recommended for You</h2>
              <p style={{ color: '#888', fontSize: '0.875rem' }}>Loading personalized recommendations...</p>
            </div>
            <LoadingSkeleton count={3} />
          </section>
        }>
          <Recommendations userId="user-123" />
        </Suspense>
        <Suspense fallback={
          <section style={{ marginBottom: '3rem' }}>
            <div style={{ marginBottom: '1.5rem' }}>
              <h2 style={{ fontSize: '1.5rem', fontWeight: 'bold' }}>Recent Orders</h2>
              <p style={{ color: '#888', fontSize: '0.875rem' }}>Loading your orders...</p>
            </div>
            <LoadingSkeleton count={3} />
          </section>
        }>
          <Orders userId="user-123" />
        </Suspense>
        <div style={{ 
          padding: '2rem', 
          background: '#1a1a1a', 
          borderRadius: '12px', 
          border: '1px solid #2a2a2a', 
          textAlign: 'center' 
        }}>
          <h3 style={{ fontSize: '1.25rem', marginBottom: '1rem', color: '#10b981' }}>
            Improved User Experience
          </h3>
          <p style={{ color: '#888', maxWidth: '600px', margin: '0 auto' }}>
            The page renders immediately with cached content, while user-specific sections
            load progressively without blocking the rest of the UI.
          </p>
        </div>
      </div>
    </>
  );
}

Kini, halaman tidak lagi dipaksa menunggu setiap sumber data sebelum dirender. Header segera muncul, dan setiap bagian berdasarkan data diizinkan memuat dengan kecepatannya sendiri. Kisi produk dibungkus dengan Suspense sehingga halaman dapat dirender bahkan pada pemuatan dingin, sementara keluaran cache-nya diselesaikan secara instan pada permintaan berikutnya. Bagian rekomendasi dan pesanan masih berjalan per permintaan, namun tidak lagi memblokir UI lainnya. Sebaliknya, placeholder muncul terlebih dahulu dan diganti segera setelah data siap.

Mulai ulang aplikasi dan navigasikan ke Kali ini, header dan kisi produk segera muncul, dan pengatur waktu mengonfirmasi bahwa konten pertama ditampilkan hampir seketika. Rekomendasi dan pesanan tidak lagi memblokir halaman. Kerangka mereka muncul terlebih dahulu, dan konten sebenarnya mengalir masuk saat sudah siap:

komponen cache perbandingan kinerja

Perbandingan kinerja

Pengguna tidak merasakan arsitektur. Mereka mengalami waktu buka. Tabel di bawah ini merangkum perilaku kedua versi selama pemuatan halaman:

Metrik Aplikasi tradisional Komponen cache
Saatnya Konten Pertama ~7 detik < 100 ms
Pengalaman Awal Layar kosong UI langsung
Pemblokiran Server Total ~7 detik Mendekati nol
Katalog Produk Blok dirender (~2 detik) Di-cache, instan saat dimuat ulang
Rekomendasi Blokir halaman (~3 detik) Streaming masuk
Pesanan Blokir halaman (~2 detik) Streaming masuk
Model Rendering Semua atau tidak sama sekali Progresif
Waktu Muat yang Dirasakan Lambat Cepat

Praktik terbaik saat menggunakan komponen cache

Komponen Cache sangat kuat, namun berfungsi paling baik bila diterapkan dengan sengaja. Berikut adalah beberapa praktik terbaik saat bekerja dengan komponen cache:

  • Jangan gabungkan API runtime dengan komponen yang di-cache
  • Jaga agar batasan Ketegangan tetap jelas dan hierarkis
  • Hanya bagian deterministik cache (misalnya, daftar produk, navigasi)
  • Pantau ukuran dan masa pakai cache untuk menghindari data basi atau penyegaran yang lambat
  • Lebih memilih tag daripada pembilasan manual untuk pembaruan terkontrol

Kesimpulan

Komponen Cache mengubah cara pengambilan keputusan rendering di Next.js. Daripada memilih antara statis dan dinamis di tingkat halaman, Anda dapat memutuskan di tingkat komponen bagian UI mana yang harus digunakan kembali dan bagian mana yang harus tetap spesifik permintaan.

Dalam contoh yang kami lalui, peningkatan tidak datang dari penulisan ulang aplikasi atau perubahan model data. Ini berasal dari pemisahan pekerjaan yang dapat diprediksi dari pekerjaan khusus pengguna dan memungkinkan masing-masing pekerjaan dirender pada timeline-nya sendiri. Komponen yang disimpan dalam cache mengurangi pekerjaan server yang berulang, sementara Suspense memastikan bahwa bagian yang lebih lambat tidak lagi memblokir sisa halaman.

Untuk detail selengkapnya tentang komponen cache, pra-render sebagian, dan API terkait, lihat dokumentasi resmi Next.js.

LogRocket: Visibilitas penuh ke dalam aplikasi produksi Next.js

Men-debug aplikasi Berikutnya bisa jadi sulit, terutama ketika pengguna mengalami masalah yang sulit untuk direproduksi. Jika Anda tertarik untuk memantau dan melacak status, menampilkan kesalahan JavaScript secara otomatis, dan melacak permintaan jaringan yang lambat serta waktu muat komponen, cobalah LogRocket.

LogRocket menangkap log konsol, kesalahan, permintaan jaringan, dan rekaman DOM dengan piksel sempurna dari sesi pengguna dan memungkinkan Anda memutarnya kembali seperti yang dilihat pengguna, menghilangkan dugaan mengapa bug terjadi — kompatibel dengan semua kerangka kerja.

Galileo AI dari LogRocket mengawasi sesi untuk Anda, secara instan mengidentifikasi dan menjelaskan kesulitan pengguna dengan pemantauan otomatis terhadap seluruh pengalaman produk Anda.

Paket middleware LogRocket Redux menambahkan lapisan visibilitas tambahan ke sesi pengguna Anda. LogRocket mencatat semua tindakan dan status dari penyimpanan Redux Anda.

Spanduk Uji Coba Gratis Dasbor LogRocket

Modernkan cara Anda men-debug aplikasi Next.js — mulai memantau secara gratis.

Berita Terkini

Berita Terbaru

Daftar Terbaru

News

Berita Terbaru

Flash News

RuangJP

Pemilu

Berita Terkini

Prediksi Bola

Togel Deposit Pulsa

Technology

Otomotif

Berita Terbaru

Daftar Judi Slot Online Terpercaya

Slot yang lagi gacor

Teknologi

Berita terkini

Berita Pemilu

Berita Teknologi

Hiburan

master Slote

Berita Terkini

Pendidikan

Resep

Jasa Backlink

One Piece Terbaru