Пошук уроків, статей та іншого контенту
Розберете кеш готових результатів рендерингу маршрутів і умови повторної генерації сторінок.
Full Route Cache — це кеш готового результату рендерингу маршруту в Next.js.
Коли маршрут є статичним, Next.js може зберегти:
HTML сторінки;
RSC Payload — дані, які використовує React Server Components.
Під час наступного запиту Next.js повертає збережений результат, не виконуючи повторний рендеринг сторінки щоразу.
Це зменшує навантаження на сервер і пришвидшує відповідь.
Розглянемо сторінку:
export default function HomePage() {
return <h1>Головна сторінка</h1>
}Якщо сторінка не використовує дані, доступні лише під час запиту, Next.js може згенерувати її наперед і зберегти результат у Full Route Cache.
Подальші запити до цього маршруту використовуватимуть уже готовий результат:
Next.js генерує сторінку.
HTML і RSC Payload потрапляють у Full Route Cache.
Наступний користувач отримує збережений результат.
Повторний рендеринг не виконується доти, доки кеш не буде очищено або оновлено.
Кешування маршруту залежить від способу його рендерингу.
Статичний маршрут можна підготувати заздалегідь:
export default function AboutPage() {
return (
<main>
<h1>Про компанію</h1>
<p>Цей текст однаковий для всіх користувачів.</p>
</main>
)
}Такий маршрут добре підходить для Full Route Cache, оскільки його результат не залежить від конкретного запиту.
Динамічний маршрут потрібно генерувати під час запиту. Наприклад, якщо сторінка використовує:
дані поточного користувача;
cookies;
заголовки запиту;
інші значення, доступні лише на сервері під час запиту;
дані, які явно не кешуються.
Динамічні маршрути зазвичай не зберігаються у Full Route Cache.
Важливо розрізняти:
динамічний маршрут — результат рендерингу створюється під час запиту;
динамічний параметр маршруту — наприклад, /products/42. Сам параметр не робить маршрут динамічним у цьому значенні. Такий маршрут усе ще може бути статичним, якщо його дані можна кешувати.
Full Route Cache зберігає результат усього маршруту, але під час його створення сторінка може отримувати дані з Data Cache.
Наприклад:
// app/products/page.tsx
type Product = {
id: number
title: string
}
async function getProducts(): Promise<Product[]> {
const response = await fetch('https://api.example.com/products', {
next: {
revalidate: 60,
},
})
if (!response.ok) {
throw new Error('Не вдалося отримати товари')
}
return response.json()
}
export default async function ProductsPage() {
const products = await getProducts()
return (
<main>
<h1>Товари</h1>
<ul>
{products.map((product) => (
<li key={product.id}>{product.title}</li>
))}
</ul>
</main>
)
}У цьому прикладі:
результат fetch може зберігатися в Data Cache;
сторінка використовує кешовані дані;
результат рендерингу маршруту може зберігатися у Full Route Cache;
дані оновлюватимуться не частіше ніж один раз на 60 секунд після появи нового запиту.
Кешування даних і кешування маршруту — це різні рівні:
Data Cache зберігає результат отримання даних;
Full Route Cache зберігає вже згенерований результат сторінки.
Для періодичного оновлення сторінки можна вказати значення revalidate на рівні маршруту:
// app/news/page.tsx
export const revalidate = 60
export default async function NewsPage() {
const response = await fetch('https://api.example.com/news', {
cache: 'no-store',
})
if (!response.ok) {
throw new Error('Не вдалося отримати новини')
}
const news: { id: number; title: string }[] = await response.json()
return (
<main>
<h1>Новини</h1>
<ul>
{news.map((item) => (
<li key={item.id}>{item.title}</li>
))}
</ul>
</main>
)
}revalidate = 60 означає, що Next.js може повторно згенерувати маршрут не частіше ніж раз на 60 секунд.
Типовий порядок роботи такий:
Next.js генерує сторінку і зберігає результат.
Протягом 60 секунд наступні запити отримують збережену версію.
Після завершення цього часу кешована версія стає застарілою.
Під час наступного звернення Next.js запускає повторну генерацію.
Новий результат замінює старий у Full Route Cache.
Цей підхід називається Incremental Static Regeneration, або ISR.
Число 60 — це секунди, а не мілісекунди:
export const revalidate = 3600 // одна годинаІноді чекати завершення інтервалу не потрібно. Наприклад, адміністратор змінив товар, і сторінку товарів потрібно оновити одразу.
Для цього використовується revalidatePath.
// app/actions.ts
'use server'
import { revalidatePath } from 'next/cache'
export async function updateProducts() {
// Тут могла б бути операція оновлення товарів у базі даних.
revalidatePath('/products')
}Після виклику revalidatePath('/products') Next.js позначає кеш маршруту /products як недійсний.
Під час наступного звернення сторінка буде згенерована повторно.
Приклад дії з кнопкою:
// app/admin/page.tsx
'use client'
import { updateProducts } from '../actions'
export default function AdminPage() {
return (
<button onClick={() => updateProducts()}>
Оновити товари
</button>
)
}У реальному застосунку серверну дію зазвичай викликають після успішного оновлення даних, а не просто після натискання кнопки.
Якщо дані, від яких залежить сторінка, було оновлено, відповідний результат маршруту може стати неактуальним.
Наприклад:
Сторінка /products отримує список товарів.
Список товарів було змінено в базі даних.
Старий результат сторінки більше не відповідає даним.
Викликається revalidatePath('/products').
Наступний запит отримує повторно згенеровану сторінку.
Якщо для отримання даних використовуються теги, можна очищати пов’язані дані за допомогою revalidateTag:
// app/actions.ts
'use server'
import { revalidateTag } from 'next/cache'
export async function refreshProducts() {
revalidateTag('products')
}Запит із таким тегом має виглядати так:
const response = await fetch('https://api.example.com/products', {
next: {
tags: ['products'],
},
})revalidateTag насамперед очищає пов’язані кешовані дані. Якщо від цих даних залежить статичний маршрут, Next.js також зможе повторно згенерувати його результат.
Результат маршруту може бути повторно згенерований у таких випадках:
завершився період revalidate;
викликано revalidatePath;
викликано revalidateTag для даних, від яких залежить маршрут;
відбулася нова генерація маршруту під час збірки;
маршрут став динамічним через спосіб отримання даних.
Зміна коду й нове розгортання також створюють нову версію застосунку, тому старий результат маршруту більше не використовується.
no-storeЯкщо дані потрібно отримувати заново для кожного запиту, можна явно вимкнути кешування:
const response = await fetch('https://api.example.com/profile', {
cache: 'no-store',
})Такий запит не використовує Data Cache. Якщо результат сторінки залежить від цього запиту, маршрут також не підходить для звичайного статичного Full Route Cache.
Це доречно для даних, які:
змінюються дуже часто;
залежать від поточного користувача;
не повинні повертатися зі старого кешу.
Водночас cache: 'no-store' не слід додавати без потреби. Якщо дані можна безпечно кешувати, кешування зазвичай покращує швидкодію.
Full Route Cache знаходиться на серверному боці Next.js. Він не є тим самим, що й кеш браузера.
У застосунку також може бути Router Cache на стороні клієнта, який зберігає вже отримані дані маршрутів під час навігації. Це інший механізм.
У цій темі важливо запам’ятати:
Full Route Cache зберігає результат серверного рендерингу;
Data Cache зберігає результати отримання даних;
браузер має власні правила кешування;
ці рівні кешу можуть оновлюватися незалежно.
Повний приклад сторінки, яка оновлюється раз на 30 секунд:
// app/catalog/page.tsx
export const revalidate = 30
type Product = {
id: number
name: string
price: number
}
async function getCatalog(): Promise<Product[]> {
const response = await fetch('https://api.example.com/catalog', {
next: {
revalidate: 30,
},
})
if (!response.ok) {
throw new Error('Помилка завантаження каталогу')
}
return response.json()
}
export default async function CatalogPage() {
const products = await getCatalog()
return (
<main>
<h1>Каталог</h1>
{products.length === 0 ? (
<p>Каталог порожній</p>
) : (
<ul>
{products.map((product) => (
<li key={product.id}>
{product.name}: {product.price} грн
</li>
))}
</ul>
)}
</main>
)
}Тут revalidate вказано і для маршруту, і для запиту. Це робить очікувану поведінку очевидною:
дані каталогу кешуються на 30 секунд;
результат сторінки також може зберігатися 30 секунд;
після цього сторінка буде оновлена під час наступного відповідного запиту.
Кеш результату fetch і кеш готової сторінки — не одне й те саме. Сторінка може містити кілька запитів із різними правилами кешування.
Якщо сторінка має revalidate = 60, зміна в базі даних не обов’язково одразу змінить готовий результат.
Для негайного очищення використовуйте revalidatePath або відповідну інвалідацію за тегом.
Маршрут /products/[id] може бути статичним, якщо його дані можна отримати та кешувати. Динамічний параметр у URL не означає автоматичну відмову від Full Route Cache.
cache: 'no-store' без потребиno-store вимикає кешування конкретного запиту. Якщо додати його до запиту без необхідності, сторінка може втратити переваги статичного рендерингу.
revalidate точним таймеромrevalidate = 60 не означає, що сторінка автоматично перегенерується рівно через 60 секунд без нового запиту. Зазвичай повторна генерація відбувається, коли після завершення інтервалу надходить нове звернення до маршруту.
Full Route Cache зберігає готові результати рендерингу маршрутів.
До результату входять HTML і RSC Payload.
Статичні маршрути можуть обслуговуватися з кешу без повторного рендерингу.
revalidate задає періодичну повторну генерацію маршруту.
revalidatePath дозволяє очистити кеш конкретного маршруту.
revalidateTag дозволяє оновити кешовані дані за тегом.
cache: 'no-store' вимикає кешування конкретного запиту.
Full Route Cache і Data Cache — різні рівні кешування.
Динамічні маршрути, які залежать від даних поточного запиту, зазвичай не зберігаються у Full Route Cache.