Кеширование fetch
Next.js расширяет fetch возможностями кеширования.
По умолчанию
fetch в Server Components кешируется автоматически:
const res = await fetch('https://api.example.com/posts');
Результат сохраняется. Повторный запрос вернёт из кеша.
Отключить кеш
const res = await fetch('https://api.example.com/posts', {
cache: 'no-store',
});
Каждый запрос идёт на сервер. Для динамических данных.
Принудительный кеш
const res = await fetch('https://api.example.com/posts', {
cache: 'force-cache',
});
Даже если данные изменились — вернёт из кеша. Явное поведение по умолчанию.
Revalidate
Кеш на время:
const res = await fetch('https://api.example.com/posts', {
next: { revalidate: 60 }, // 60 секунд
});
Через 60 секунд данные перезагрузятся.
revalidate: 0 — то же, что cache: 'no-store'.
revalidate: false — кеш навсегда.
Теги
Группировка запросов:
const res = await fetch('https://api.example.com/posts', {
next: { tags: ['posts'] },
});
Перегенерация:
import { revalidateTag } from 'next/cache';
revalidateTag('posts');
Все запросы с тегом posts обновятся.
Несколько тегов:
next: { tags: ['posts', 'homepage'] }
revalidatePath
Обновить целую страницу:
import { revalidatePath } from 'next/cache';
revalidatePath('/posts');
revalidatePath('/posts/[id]', 'page'); // динамический
После мутации
'use server';
import { revalidatePath, revalidateTag } from 'next/cache';
export async function createPost(data: FormData) {
await db.post.create({ data: { title: data.get('title') } });
revalidatePath('/posts');
revalidateTag('posts');
}
Статическая vs динамическая
Статическая страница — генерируется при сборке, кешируется навсегда (до rebuild).
Динамическая — при каждом запросе. Если использовать cache: 'no-store', cookies(), headers(), searchParams — становится динамической.
Route Segment Config
Управление на уровне файла:
// app/posts/page.tsx
export const dynamic = 'force-dynamic'; // всегда динамическая
export const revalidate = 60; // ISR на 60 секунд
export const fetchCache = 'force-no-store';
Пример: блог с ISR
export const revalidate = 3600; // 1 час
export default async function Blog() {
const res = await fetch('https://api.example.com/posts', {
next: { revalidate: 3600, tags: ['posts'] },
});
const posts = await res.json();
return <PostList posts={posts} />;
}
Когда что
- Статические данные (о компании) —
force-cache,revalidate: false - Редко меняющиеся (блог, каталог) — ISR с
revalidate: 3600 - Часто меняющиеся (лента, комментарии) —
revalidate: 60или теги - Персональные (профиль, корзина) —
no-store
Итоги
fetchкешируется по умолчаниюcache: 'no-store'для динамикиnext: { revalidate: N }для ISRnext: { tags: [...] }для группировкиrevalidateTagиrevalidatePathпосле мутаций- Route Segment Config для уровня файла