Пошук уроків, статей та іншого контенту
Розберете рівні кешу Next.js, їхнє призначення та шлях даних від запиту до браузера.
Кешування — це збереження вже отриманих або обчислених даних, щоб не виконувати ту саму роботу повторно.
У Next.js кешування може відбуватися на кількох рівнях:
Request Memoization — повторне використання однакових запитів під час одного рендерингу.
Data Cache — збереження результатів отримання даних на сервері.
Full Route Cache — збереження результату рендерингу маршруту.
Router Cache — кешування даних маршруту в браузері під час навігації.
Ці рівні працюють разом, але мають різне призначення.
Кеш Next.js — це не те саме, що кеш браузера. Браузер також може кешувати HTTP-відповіді, але це окремий механізм.
Для сторінки з App Router шлях даних приблизно такий:
Користувач відкриває сторінку або переходить на неї.
Next.js перевіряє клієнтський Router Cache.
Якщо потрібних даних немає в Router Cache, браузер надсилає запит на сервер.
Сервер перевіряє Full Route Cache, якщо маршрут може бути статичним.
Під час рендерингу сервер перевіряє Data Cache для запитів до даних.
Якщо даних немає в Data Cache, Next.js виконує запит до API або бази даних.
Next.js формує HTML і React Server Components Payload.
Результат може бути збережений у Full Route Cache.
Відповідь надходить у браузер і потрапляє до Router Cache.
Не кожен запит проходить усі рівні. Наприклад, динамічна сторінка може не використовувати Full Route Cache, але все одно використовувати Data Cache для окремого запиту.
Request Memoization — це автоматичне усунення однакових запитів під час одного рендерингу.
Уявімо, що два компоненти на одній сторінці викликають однаковий fetch:
const response = await fetch('https://api.example.com/user/1');Next.js може виконати цей запит лише один раз, а результат використати в обох компонентах.
Це корисно, коли:
кілька компонентів потребують однакових даних;
компоненти розташовані на різних рівнях дерева;
ви не хочете вручну передавати дані через багато компонентів.
Request Memoization працює лише протягом одного серверного рендерингу. Це не довготривале сховище. Для кешування між різними запитами використовується Data Cache.
Data Cache зберігає результати отримання даних на сервері. Наприклад, Next.js може зберегти результат fetch, щоб під час наступного запиту не звертатися до зовнішнього API.
Для кешування з оновленням раз на певний час можна передати параметр next.revalidate:
const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
next: {
revalidate: 60,
},
});У цьому прикладі результат може використовуватися протягом 60 секунд. Після цього Next.js оновить дані під час наступного відповідного запиту.
Файл app/posts/page.js:
async function getPosts() {
const response = await fetch(
'https://jsonplaceholder.typicode.com/posts?_limit=5',
{
next: {
revalidate: 60,
tags: ['posts'],
},
}
);
if (!response.ok) {
throw new Error('Не вдалося завантажити публікації');
}
return response.json();
}
export default async function PostsPage() {
const posts = await getPosts();
return (
<main>
<h1>Публікації</h1>
<ul>
{posts.map((post) => (
<li key={post.id}>
<h2>{post.title}</h2>
<p>{post.body}</p>
</li>
))}
</ul>
</main>
);
}У цьому прикладі:
дані завантажуються на сервері;
результат кешується на 60 секунд;
до кешованого запису додано тег posts;
браузер не виконує цей fetch напряму.
Якщо дані повинні запитуватися щоразу, використовуйте cache: 'no-store':
async function getCurrentPrice() {
const response = await fetch('https://api.example.com/price', {
cache: 'no-store',
});
if (!response.ok) {
throw new Error('Не вдалося отримати ціну');
}
return response.json();
}Такий запит не зберігається в Data Cache.
Це може бути потрібно для:
поточного курсу валюти;
приватних даних користувача;
даних, які змінюються майже миттєво;
інформації, яку не можна показувати з кешу.
У різних версіях Next.js значення кешування за замовчуванням для fetch могли змінюватися. Тому для важливих запитів краще явно вказувати cache або next.revalidate.
Full Route Cache зберігає результат рендерингу маршруту на сервері.
Для статичного маршруту Next.js може зберегти:
HTML;
React Server Components Payload;
інформацію, потрібну для повторного показу сторінки.
Після цього наступним відвідувачам не потрібно щоразу повністю рендерити сторінку з нуля.
Наприклад, сторінка зі списком публікацій може:
один раз отримати дані;
відрендерити HTML;
зберегти результат;
повертати його наступним користувачам до моменту оновлення кешу.
Якщо сторінка використовує кешований fetch із revalidate, її результат також може бути частиною Full Route Cache.
Маршрут може бути:
статичним — результат можна підготувати наперед і кешувати;
динамічним — результат потрібно формувати для кожного запиту.
Динамічний рендеринг може бути потрібний, якщо сторінка залежить від:
cookies;
заголовків конкретного запиту;
даних поточного користувача;
запитів із cache: 'no-store'.
Динамічний маршрут зазвичай не зберігається у Full Route Cache. Водночас окремі запити всередині нього все ще можуть використовувати Data Cache.
Router Cache знаходиться в пам’яті браузера та використовується під час навігації між маршрутами Next.js.
Він допомагає:
не завантажувати вже відвідану сторінку повторно;
швидше переходити між маршрутами;
зберігати частини дерева маршруту;
використовувати отриманий React Server Components Payload.
Наприклад, користувач відкрив:
/
/posts
повернувся на /
Під час повернення Next.js може використати дані з Router Cache, якщо вони ще актуальні.
Router Cache належить клієнту, а Data Cache і Full Route Cache працюють на стороні сервера. Це різні кеші, тому очищення одного з них не завжди очищає інші.
Розглянемо сторінку, яка отримує список товарів із повторним оновленням кожні 5 хвилин:
async function getProducts() {
const response = await fetch('https://api.example.com/products', {
next: {
revalidate: 300,
},
});
return response.json();
}Поведінка може бути такою:
Перший запит доходить до сервера.
Next.js отримує товари із зовнішнього API.
Результат зберігається в Data Cache.
Сторінка рендериться.
Результат маршруту може бути збережений у Full Route Cache.
Дані сторінки надходять у браузер і потрапляють до Router Cache.
Наступна навігація може використати Router Cache.
Якщо потрібен новий серверний рендеринг, Next.js може використати Data Cache замість повторного звернення до API.
Після завершення 300 секунд Next.js зможе отримати свіжі дані. При цьому точний момент оновлення залежить від способу інвалідації та типу маршруту.
Кешовані дані потрібно оновлювати, коли змінюються дані в базі або зовнішній системі.
Для цього Next.js має функції інвалідації, зокрема:
revalidatePath — оновити кеш конкретного маршруту;
revalidateTag — оновити дані, пов’язані з тегом.
Наприклад, запит у попередньому прикладі має тег posts:
const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
next: {
revalidate: 60,
tags: ['posts'],
},
});Після зміни публікацій серверний код може інвалідувати дані з цим тегом:
import { revalidateTag } from 'next/cache';
revalidateTag('posts');Після цього наступний запит зможе отримати свіжі дані.
revalidateTag потрібно викликати на сервері, наприклад у Server Action або Route Handler. Викликати його безпосередньо з клієнтського компонента не можна.
Кожен рівень кешу вирішує окрему задачу:
Request Memoization не дає повторно виконати однаковий запит під час одного рендерингу.
Data Cache зберігає отримані дані на сервері.
Full Route Cache зберігає результат рендерингу маршруту.
Router Cache зберігає частини маршрутів у браузері.
Наприклад, якщо ви вимкнули Data Cache за допомогою cache: 'no-store', це не означає, що браузер або Router Cache автоматично забудуть уже відкриту сторінку.
Якщо використати:
fetch(url, {
next: {
revalidate: 3600,
},
});дані можуть залишатися кешованими до однієї години.
Для даних, які повинні бути актуальними під час кожного запиту, використовуйте:
fetch(url, {
cache: 'no-store',
});Код із fetch і параметрами кешування зазвичай потрібно розміщувати в Server Component або іншому серверному коді.
Не варто покладатися на Data Cache безпосередньо з клієнтського компонента.
Router Cache знаходиться в браузері, а Data Cache — на сервері.
Очищення або оновлення даних на сервері не завжди негайно змінює вже відкритий маршрут у браузері. Під час навігації може використовуватися вже збережений Router Cache.
cache: 'no-store' гарантує свіжі дані, але кожен запит вимагатиме нового звернення до джерела даних.
Для даних, які не змінюються часто, ефективніше використати revalidate.
Не можна бездумно кешувати дані, що залежать від конкретного користувача. Наприклад, сторінка профілю не повинна повертати одному користувачу кешований результат, сформований для іншого.
Для таких даних потрібно уважно визначити, чи має запит бути динамічним і чи можна використовувати кеш.
Next.js має кілька рівнів кешування, і кожен відповідає за власний етап.
Request Memoization усуває дублікати запитів під час одного рендерингу.
Data Cache зберігає результати отримання даних на сервері.
Full Route Cache зберігає результат рендерингу статичних маршрутів.
Router Cache прискорює навігацію в браузері.
next.revalidate задає час повторного оновлення кешованих даних.
cache: 'no-store' вимикає кешування конкретного запиту.
revalidatePath і revalidateTag дають змогу інвалідувати кеш після зміни даних.
Для правильного кешування потрібно розуміти, де виконуються дані: у браузері, на сервері під час рендерингу чи під час отримання даних.