Dziennik / Rozwój produktów webowych

Jak działa pamięć podręczna w Next.js v13+ z Routerem aplikacji

Jak działa pamięć podręczna w Next.js v13+ z Routerem aplikacji

Szczegółowe omówienie działania pamięci podręcznej w Next.js 13+, jakie rodzaje pamięci podręcznej istnieją, oraz jak wpływa to na wydajność i SEO.

Co to jest pamięć podręczna i jak działa w Next.js

Pamięć podręczna służy do przechowywania wyników obliczeń lub żądań, dzięki czemu nie trzeba ich powtarzać, co przyspiesza działanie aplikacji. W Next.js, strony HTML, dane, żądania, renderowanie komponentów oraz nawet całe trasy mogą być przechowywane w pamięci podręcznej.

Z wprowadzeniem Routera aplikacji (od Next.js 13) filozofia się zmieniła: pamięć podręczna jest teraz domyślnie włączona, a to od programisty zależy, czy ją wyłączyć, gdy potrzebne jest zachowanie dynamiczne.

Polityka Next.js jest następująca:

"Cachujemy wszystko, chyba że inaczej zasugerowano"

Jeśli nie określisz jawnie cache: 'no-store', next: { revalidate }, lub dynamic = 'force-dynamic', to:

  • fetch() będzie przechowywane w pamięci podręcznej
  • układ i strona mogą być również przechowywane w pamięci podręcznej
  • trasy zostaną dodane do pamięci podręcznej trasy
  • renderowanie strony przejdzie do pełnej pamięci podręcznej trasy

Zapewnia to wydajność, ale może prowadzić do przestarzałych danych i problemów z debugowaniem.

Rodzaje pamięci podręcznej w Next.js

Źródło: Dokumentacja Next.js

Pełna pamięć podręczna trasy (pamięć podręczna trasy po stronie serwera)

Używana z ISR (Inkrementalna Statyczna Regeneracja).

  • Cała strona jest przechowywana w pamięci podręcznej (HTML, dane z layout.tsx, page.tsx, fetch(), headers(), cookies()).
  • Pamięć podręczna znajduje się na serwerze.
  • Działa domyślnie, gdy nie ma zachowania dynamicznego.
  • Wyłączona za pomocą revalidate lub revalidateTag().

Najlepsza dla blogów, stron marketingowych i innych treści rzadko zmieniających się.

Przykład:

export const revalidate = 60; // regenerate the page every 60 seconds

Pamięć podręczna danych (pamięć podręczna pobierania danych po stronie serwera)

  • Odpowiedzi z fetch() w Komponentach Serwera są przechowywane w pamięci podręcznej.
  • Kontrolowana za pomocą next: { revalidate } lub cache: 'no-store'.

Przykład:

await fetch('https://api.com/posts', {
  next: { revalidate: 300 },
});

Memoizacja żądania (pamięć podręczna na żądanie, po stronie serwera)

Jest to optymalizacja wewnętrzna Next.js: ta sama fetch() lub funkcja z tymi samymi argumentami jest wywoływana tylko raz na renderowanie.

  • W jednym renderowaniu serwera Next.js śledzi identyczne żądania.
  • Jeśli zostały już wywołane, wynik jest pobierany z pamięci podręcznej.
  • Działa niezależnie od cache: 'force-cache' lub no-store.

Dobra dla:

  • DRY (nie powtarzaj się) podczas pobierania tych samych danych w różnych komponentach.
  • Poprawa wydajności bez konieczności manualnej optymalizacji.
  • Gdy ten sam fetch jest używany zarówno w layout.tsx jak i page.tsx.

Nie dla:

  • Ponowne wykorzystanie między różnymi żądaniami lub użytkownikami (nie jest globalną pamięcią podręczną).
  • Omijanie pamięci podręcznej pobierania danych - działa na bazie Memoizacji Żądań.
// lib/getUser.ts
export async function getUser() {
  return fetch('https://api.com/user').then(res => res.json());
}

// app/layout.tsx
export default async function Layout({ children }) {
  const user = await getUser(); // Call 1
  return (
    <div>
      <Sidebar user={user} />
      {children}
    </div>
  );
}

// app/page.tsx
import { getUser } from '../lib/getUser';

export default async function Page() {
  const user = await getUser(); // Call 2, but actually not repeated
  return <p>Hello, {user.name}</p>;
}

Fizycznie, fetch zostanie wywołany tylko raz, a wynik zostanie użyty w obu miejscach.

Pamięć podręczna routera (pamięć podręczna po stronie klienta)

To pamięć podręczna, którą kontrolujesz samodzielnie za pomocą bibliotek takich jak SWR, React Query i inne.

  • Działa tylko z nawigacją po stronie klienta (<Link>, router.push()).
  • Next.js nie kontroluje tej pamięci podręcznej — decydujesz, jak ją pamiętać.
  • Bardzo szybka, ponieważ nic nie jest ponownie renderowane — React po prostu montuje z pamięci.
  • Ale istnieje tylko do momentu, gdy użytkownik przeładuje stronę (F5).

Najlepsza dla interaktywnych komponentów, formularzy, pulpitów nawigacyjnych.

Przykład:

'use client';
import useSWR from 'swr';

const { data } = useSWR('/api/data', fetcher);

Wizualne porównanie rodzajów pamięci podręcznej

Rodzaj pamięci podręcznejGdzie działaKontrolaPrzykład
Pełna pamięć podręczna trasySerwer (cała trasa)ponowna walidacja, tageksport const revalidate = 60
Pamięć podręczna danychSerwer (pobieranie)pamięć podręczna, ponowna walidacjapobierz(..., { next: ... })
Pamięć podręczna generowana statycznieSerwer (czas budowy)BrakJSX bez danych
Pamięć podręczna po stronie klientaPrzeglądarka / klientTySWR / React Query

Przykład życia codziennego

Załóżmy, że budujesz sklep:

StronaCo zapamiętaćJaki rodzaj pamięci podręcznej użyć
/produktyLista produktów, aktualizacja co 5 minutPełna trasa pamięci podręcznej (ISR)
/api/produktyDane API produktuPamięć podręczna danych (z revalidateTag)
Nawigacja SPASzybka nawigacjaPamięć podręczna routera (pamięć podręczna po stronie klienta)

Typowe problemy z pamięcią podręczną w aplikacji Router

Zaktualizowane dane — ale strona pokazuje stare dane

fetch() używa force-cache domyślnie, jeśli nie określisz cache: 'no-store' lub next: { revalidate }. W rezultacie Next.js zapamiętuje dane i nigdy ich nie aktualizuje.

Rozwiązanie:

  • Dodaj cache: 'no-store' aby wyłączyć buforowanie
  • Lub next: { revalidate: 60 } dla ISR
fetch('https://api.com/data', { next: { revalidate: 60 } });

URL (searchParams) się zmienił, ale dane są takie same

Next.js nie ponownie generuje żądania pobierania, jeśli URL jest taki sam. Jeśli używasz parametrów, dodaj je bezpośrednio do adresu URL pobierania:

fetch(`https://api.com/posts?category=${searchParams.category}`, {
  next: { revalidate: 60 }
});

Nawigacja za pomocą <Link>, ale dane się nie zaktualizowały

Pamięć podręczna routera (pamięć podręczna po stronie klienta) została użyta. Next.js nie wysyła nowego żądania podczas powrotu na stronę.

Rozwiązanie:

  • Wymuś odświeżenie danych za pomocą router.refresh()
  • Użyj cache: 'no-store' jeśli dane muszą być zawsze świeże

revalidatePath() lub revalidateTag() nie działa

  • Pamięć podręczna nie została utworzona (np. z powodu no-store)
  • Nie używasz tagów w pobieraniu
  • Wywołujesz revalidatePath() poza akcją serwera

Rozwiązanie:

  • Upewnij się, że pobieranie używa next: { tags: [...] }
  • Wywołaj revalidateTag('tag') w funkcji serwera

Układ jest buforowany i nie aktualizuje się

layout.tsx jest także buforowany jako RSC z revalidate, a jeśli dane wewnątrz są buforowane, nie zostaną zaktualizowane aż do następnego ISR.

Rozwiązanie:

  • Użyj cache: 'no-store' dla pobierania w layout.tsx, jeśli na przykład zawiera profil użytkownika
  • Albo przenieś pobieranie z układu do strony (lub komponentu klienta z SWR)

Niespodziewane zachowanie w trybie developerskim (dev)

W next dev, wiele pamięci podręcznej jest wyłączonych lub działa inaczej, a revalidate nie działa niezawodnie w trybie deweloperskim.

Wskazówka:

Przetestuj pamięć podręczną z kolejną kompilacją && uruchomieniem next lub podglądem Vercel.

Kombinacja cookies(), headers(), params i fetch() psuje pamięć podręczną.

Kiedy używasz dynamicznych rzeczy jak:

cookies()

headers()

useSearchParams()

generateMetadata() z dynamicznymi

Next.js automatycznie wyłącza pamięć podręczną strony i przełącza się na dynamic = 'force-dynamic'. Lista kontrolna: jak unikać problemów z pamięcią podręczną

Zawsze jasno ustawiaj zachowanie pobierania:

  • Nie zapomnij o bezpośrednim uwzględnieniu parametrów zapytania w adresie URL pobierania
  // no cache at all
  fetch(..., { cache: 'no-store' })

  // ISR (revalidate every 60 seconds)
  fetch(..., { next: { revalidate: 60 } })

  // manual invalidation via tag
  fetch(..., { next: { tags: ['products'] } })
  • Przetestuj z
  • kolejną kompilacją && uruchomieniem next — w przeciwnym razie możesz mieć fałszywe poczucie pewności Jeśli musisz ręcznie unieważnić dane, użyj
  • revalidatePath()lubrevalidateTag() Jak działa pamięć podręczna w Next.js v13+ z App Router