Загрузка...
Загрузка...
Продолжая работу с платформой, вы принимаете условия Политики конфиденциальности и использование файлов cookie.
ISR (Incremental Static Regeneration) это стратегия, которая объединяет преимущества SSG и SSR. Страница генерируется статически, но может обновляться в фоне через заданный интервал без полной пересборки приложения.
Представь, что на Hack Frontend есть каталог задач. Он обновляется пару раз в день. SSG не подходит, потому что после добавления новой задачи нужно делать полный next build. SSR не подходит, потому что каталог одинаков для всех пользователей и нет смысла генерировать его при каждом запросе.
ISR решает эту дилемму: страница статическая, но умеет обновляться.
Страница генерируется статически, как при SSG. HTML кешируется.
Пользователь получает кешированную версию страницы. Никакого рендеринга на сервере.
Пользователь все еще получает кешированную версию (stale), но Next.js запускает регенерацию в фоне. Следующий пользователь получит уже обновленную страницу.
Этот подход называется stale-while-revalidate: отдаем устаревшее, пока готовим свежее.
В App Router ISR настраивается через параметр revalidate в fetch:
// app/problems/page.tsx
export default async function ProblemsPage() {
const res = await fetch('https://api.hackfrontend.com/problems', {
next: { revalidate: 300 }
})
const problems = await res.json()
return (
<div>
<h1>Задачи на Hack Frontend</h1>
<ul>
{problems.map((p: { id: string; name: string }) => (
<li key={p.id}>{p.name}</li>
))}
</ul>
</div>
)
}
revalidate: 300 значит: кешировать страницу на 5 минут, после чего обновить в фоне.
Можно задать revalidate для всего роута:
// app/problems/layout.tsx
export const revalidate = 300
export default function ProblemsLayout({
children
}: {
children: React.ReactNode
}) {
return <>{children}</>
}
Вместо интервала можно обновлять страницу по событию. Например, после добавления новой задачи:
// app/api/revalidate/route.ts
import { revalidatePath, revalidateTag } from 'next/cache'
import { NextResponse } from 'next/server'
export async function POST(request: Request) {
const { secret, path } = await request.json()
if (secret !== process.env.REVALIDATION_SECRET) {
return NextResponse.json({ error: 'Invalid secret' }, { status: 401 })
}
revalidatePath(path)
return NextResponse.json({ revalidated: true })
}
Или с использованием тегов:
// Загрузка данных с тегом
const res = await fetch('https://api.hackfrontend.com/problems', {
next: { tags: ['problems'] }
})
// Инвалидация по тегу после обновления данных
revalidateTag('problems')
На практике:
On-demand revalidation удобнее, чем интервальный подход. Ты обновляешь страницу именно тогда, когда данные изменились, а не через фиксированный промежуток.
В Pages Router ISR включается добавлением revalidate в getStaticProps:
// pages/problems.tsx
export async function getStaticProps() {
const res = await fetch('https://api.hackfrontend.com/problems')
const problems = await res.json()
return {
props: { problems },
revalidate: 300
}
}
Подходит:
Не подходит:
| SSG | ISR | SSR | |
|---|---|---|---|
| Генерация | При сборке | При сборке + фоновое обновление | При каждом запросе |
| Актуальность | На момент сборки | С задержкой (revalidate) | Всегда актуальные |
| Скорость | Мгновенная | Мгновенная | Зависит от сервера |
| Пересборка | Полная | Не нужна | Не нужна |