JavaRush /Курсы /Модуль 4: Node.js, Next.js и Angular /Инкрементальная статическая регенерация (ISR) с

Инкрементальная статическая регенерация (ISR) с revalidate в Next.js

Модуль 4: Node.js, Next.js и Angular
9 уровень , 5 лекция
Открыта

1. Введение

Давайте вспомним, что такое статическая генерация (Static Site Generation, SSG). Когда вы билдите проект с SSG, Next.js заранее генерирует HTML для всех страниц на этапе сборки (next build). Это прекрасно для "вечных" страниц: например, раздел "О компании", список услуг, портфолио. Но что делать, если данные на странице должны иногда обновляться, например, раз в час, но вы не хотите запускать полный билд сайта вручную?

Вот тут-то и появляется ISR — механизм, который позволяет автоматически обновлять уже сгенерированные страницы по расписанию.

Аналогия из жизни
Представьте, что вы печатаете газету. Если бы вы всегда печатали только один выпуск и никогда его не обновляли, это был бы SSG. Если бы вы печатали новый выпуск при каждом запросе читателя — это SSR (Server Side Rendering). А ISR — это компромисс: вы печатаете газету заранее, но через некоторое время выпускаете свежий номер, не останавливая печатный станок и не перепечатывая все предыдущие выпуски.

Что такое ISR и зачем он нужен?

ISR (Incremental Static Regeneration) — это технология Next.js, позволяющая:

  • Генерировать страницы статически (быстро отдавать их пользователю).
  • Автоматически обновлять (регенерировать) эти страницы на сервере через заданный интервал времени.
  • Не пересобирать весь сайт целиком — только нужную страницу.

Ключевые плюсы:

  • Пользователь всегда получает страницу быстро, из кэша.
  • Данные на странице могут быть относительно свежими (например, обновляться каждые 10 минут).
  • Нет необходимости вручную запускать билд сайта при каждом изменении данных.

Как работает ISR: схема

Давайте разберёмся, как это работает "под капотом":

  1. Первый пользователь заходит на страницу. Если страницы ещё нет в кэше, Next.js генерирует её на сервере, кладёт в кэш и отдаёт пользователю.
  2. Следующие пользователи получают уже сгенерированную (закэшированную) версию страницы — это очень быстро!
  3. По истечении времени revalidate (например, 60 секунд), при очередном заходе пользователя Next.js отдаёт ему всё ещё старую страницу, но в фоне начинает регенерировать новую версию.
  4. Когда регенерация завершена, следующему пользователю отдаётся уже обновлённая страница.
flowchart LR
    A[Пользователь заходит на страницу] --> B{Есть кэш?}
    B -- Нет --> C[Генерируем страницу]
    C --> D[Сохраняем в кэш и отдаём пользователю]
    B -- Да --> E[Отдаём закэшированную страницу]
    E --> F{Время revalidate истекло?}
    F -- Нет --> G[Всё, пользователь доволен]
    F -- Да --> H[В фоне: регенерируем страницу]
    H --> I[Обновляем кэш]
    I --> G
  

Схема работы ISR в Next.js

2. Как использовать ISR в Next.js 15 (App Router)

Использование параметра revalidate в fetch

В Server ComponentsRoute Handlers) вы можете управлять кешированием с помощью опции revalidate:


// app/page.tsx
export default async function Page() {
  const res = await fetch('https://api.example.com/posts', {
    next: { revalidate: 60 } // 60 секунд
  });
  const posts = await res.json();

  return (
    <main>
      <h1>Новости</h1>
      <ul>
        {posts.map(post => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </main>
  );
}

Что это значит?

  • Страница будет обновляться не чаще, чем раз в 60 секунд.
  • Все пользователи внутри этого окна времени получат одну и ту же версию страницы (из кэша).
  • Через 60 секунд при следующем запросе начнётся регенерация (ISR).

Использование revalidate для всей страницы

Можно задать revalidate для всей страницы, если вы не используете fetch:


// app/page.tsx
export const revalidate = 120; // 2 минуты

export default async function Page() {
  // ...какой-то статический контент...
}

3. Практика: интеграция ISR в учебное приложение

Допустим, в вашем учебном проекте есть страница "Список студентов", данные для которой приходят из внешнего API.

Шаг 1. Пример кода с ISR


// app/students/page.tsx
export default async function StudentsPage() {
  const res = await fetch('https://randomuser.me/api/?results=5', {
    next: { revalidate: 300 } // обновлять каждые 5 минут
  });
  const data = await res.json();

  return (
    <div>
      <h2>Студенты группы 101</h2>
      <ul>
        {data.results.map((student: any) => (
          <li key={student.login.uuid}>
            {student.name.first} {student.name.last}
          </li>
        ))}
      </ul>
      <p><small>Список обновляется каждые 5 минут.</small></p>
    </div>
  );
}

Шаг 2. Проверяем в браузере

  • Откройте страницу /students.
  • Скопируйте имена студентов.
  • Подождите 5 минут, обновите страницу — увидите новые имена (данные обновились).

Шаг 3. Как проверить, что ISR работает

  • Попробуйте изменить интервал в revalidate на 10 секунд и повторите эксперимент.
  • Откройте DevTools → Network и посмотрите, что страница не делает новый fetch каждую загрузку — данные берутся из кэша, пока не истечёт время.

4. Полезные нюансы

Где ISR особенно полезен

ISR отлично подходит для страниц, которые:

  • Должны быть быстрыми (отдача из кэша).
  • Обновляются не слишком часто (раз в N минут/часов/суток).
  • Не требуют мгновенного отображения новых данных (например, новостные ленты, каталоги товаров, блоги, "топ-10" и т.д.).
Примеры
Страница Подходит для ISR? Почему
Главная сайта Да Обновляется редко, нужен быстрый отклик
Страница товара Да Цена меняется не каждую секунду
Корзина пользователя Нет Должна быть всегда актуальной
Профиль пользователя Нет Персональные, часто меняющиеся данные

Сравнение: SSG, SSR, ISR, CSR

Подход Когда генерируется страница Скорость отдачи Актуальность данных Подходит для
SSG На этапе сборки (next build) Молниеносно Только при новом билде Вечные страницы, блог, FAQ
SSR При каждом запросе пользователя Медленнее Всегда свежие Дашборды, корзины, профили
ISR На первом запросе, далее — по таймеру Быстро Почти свежие Новости, каталоги, топы
CSR В браузере, после загрузки JS Быстро (зависит от клиента) Требует загрузки данных на клиенте Интерактивные SPA

Особенности и нюансы работы с ISR

  • Какой интервал указывать?
    Если данные обновляются редко — ставьте большее значение (например, 3600 секунд = 1 час). Если нужна почти "живость" — ставьте меньше (10-30 секунд). Не ставьте слишком маленькие значения без нужды: это увеличит нагрузку на сервер и может привести к блокировкам со стороны API.
  • Что происходит, если запрос к API упал во время регенерации?
    Если во время регенерации данных произошла ошибка, Next.js продолжит отдавать старую (предыдущую) версию страницы из кэша. Это защищает пользователей от "падающих" страниц при временных сбоях.

5. Типичные ошибки при работе с ISR и revalidate

Ошибка №1: Ожидание мгновенного обновления данных после изменения источника

ISR работает по таймеру. Если вы обновили данные в базе, но время revalidate ещё не истекло, пользователи будут видеть старую страницу. Это не баг, а фича. Если нужна мгновенная актуализация — используйте SSR или ручное инвалидирование кэша.

Ошибка №2: Использование слишком короткого интервала revalidate

Студенты часто ставят revalidate: 1 (секунда), ожидая "живые" данные. Но это может привести к DDoS вашего собственного API или превышению лимитов внешнего сервиса. Не делайте так без необходимости.

Ошибка №3: Попытка использовать ISR для персональных страниц

ISR подходит только для страниц, которые одинаковы для всех пользователей. Для персональных (например, "Моя корзина") используйте SSR или CSR.

Ошибка №4: Неочевидная работа кэша при разработке

В режиме разработки (next dev) Next.js часто отключает кэширование, чтобы вы всегда видели свежие данные. Проверяйте работу ISR в production-сборке (next build + next start).

Ошибка №5: Забыли про ограничения fetch

Если вы используете кастомные HTTP-заголовки или нестандартные методы, убедитесь, что fetch с ISR поддерживает их. Иначе страница может не кэшироваться как ожидается.

Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ