Разработка ISR (Incremental Static Regeneration) для сайта

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Разработка ISR (Incremental Static Regeneration) для сайта
Сложная
~3-5 рабочих дней
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    874
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    851

Разработка ISR (Incremental Static Regeneration) для сайта

Incremental Static Regeneration — это SSG с возможностью обновления отдельных страниц без полной пересборки сайта. Страница генерируется один раз, кэшируется, а при устаревании кэша — регенерируется в фоне при следующем запросе. Пользователь всегда получает ответ мгновенно из кэша, а свежесть контента гарантируется TTL.

ISR занимает нишу между SSG (мгновенная отдача, устаревший контент) и SSR (свежий контент, латентность сервера при каждом запросе).

Как работает ISR

Классическая модель (Stale-While-Revalidate на уровне страниц):

  1. Первый запрос к странице — рендер на сервере, кэширование HTML
  2. Повторные запросы в течение TTL — отдача из кэша, ответ за < 10ms
  3. Запрос после истечения TTL — отдача устаревшего кэша (пользователь не ждёт), запуск фоновой регенерации
  4. Следующий запрос — свежий HTML из обновлённого кэша

Результат: TTFB как у статики, свежесть контента как у SSR.

Реализация в Next.js App Router

// app/products/[id]/page.tsx
interface Props {
  params: { id: string };
}

async function getProduct(id: string): Promise<Product | null> {
  const res = await fetch(`https://api.example.com/products/${id}`, {
    next: {
      revalidate: 300, // Регенерация не чаще раза в 5 минут
      tags: [`product-${id}`], // Тег для on-demand revalidation
    },
  });
  if (!res.ok) return null;
  return res.json();
}

export default async function ProductPage({ params }: Props) {
  const product = await getProduct(params.id);
  if (!product) notFound();

  return <ProductView product={product} />;
}

// Предварительная генерация популярных страниц при сборке
export async function generateStaticParams() {
  const popularProducts = await fetch('https://api.example.com/products?popular=true&limit=100')
    .then(r => r.json());

  return popularProducts.map(({ id }: { id: string }) => ({ id }));
}

Страницы из generateStaticParams генерируются при сборке. Все остальные — при первом запросе (on-demand) и затем регенерируются по TTL.

On-Demand Revalidation — немедленное обновление

TTL-кэш не подходит, когда нужно обновить страницу сразу после изменения в CMS или базе. Для этого — on-demand revalidation через API:

// app/api/revalidate/route.ts
import { revalidateTag, revalidatePath } from 'next/cache';
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
  const secret = request.headers.get('x-revalidate-secret');

  if (secret !== process.env.REVALIDATE_SECRET) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
  }

  const { tag, path } = await request.json();

  if (tag) {
    revalidateTag(tag); // Инвалидировать всё с этим тегом
  }

  if (path) {
    revalidatePath(path); // Инвалидировать конкретный путь
  }

  return NextResponse.json({ revalidated: true, at: new Date().toISOString() });
}

Webhook из CMS вызывает этот endpoint при публикации:

# Пример запроса из Contentful webhook
curl -X POST https://example.com/api/revalidate \
  -H "x-revalidate-secret: ${REVALIDATE_SECRET}" \
  -H "Content-Type: application/json" \
  -d '{"tag": "product-42"}'

Реализация в Nuxt 3

<!-- pages/products/[id].vue -->
<script setup lang="ts">
const route = useRoute();

// Nuxt cachedFetch с TTL
const { data: product } = await useFetch<Product>(
  `/api/products/${route.params.id}`,
  {
    key: `product-${route.params.id}`,
    getCachedData: (key, nuxtApp) => nuxtApp.payload.data[key],
  }
);

// server/api/products/[id].ts использует cache() из nitro
</script>
// server/api/products/[id].ts
import { defineEventHandler, getRouterParam } from 'h3';

export default cachedEventHandler(
  async (event) => {
    const id = getRouterParam(event, 'id');
    return await $fetch(`https://api.example.com/products/${id}`);
  },
  {
    maxAge: 300,
    staleMaxAge: 3600,
    name: 'product',
    getKey: (event) => `product-${getRouterParam(event, 'id')}`,
  }
);

Стратегии кэширования

ISR позволяет задавать разные TTL для разных типов страниц:

Тип страницы TTL Логика
Главная страница 60 сек Обновляется часто
Страница категории 300 сек Меняется при добавлении товаров
Страница товара 3600 сек Данные стабильные, цена — отдельный запрос
Статья блога 86400 сек Редко редактируется
Документация On-demand only Только при публикации

Кэш-хранилище для распределённого деплоя

Vercel использует edge-кэш встроенно. Для self-hosted Next.js нужен внешний кэш:

// next.config.ts
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
  cacheHandler: process.env.NODE_ENV === 'production'
    ? require.resolve('./cache-handler.js')
    : undefined,
  cacheMaxMemorySize: 0, // Отключить in-memory кэш при внешнем handler
};

// cache-handler.js — Redis-бэкэнд
const redis = require('ioredis');
const client = new redis(process.env.REDIS_URL);

module.exports = class CacheHandler {
  async get(key) {
    const data = await client.get(key);
    return data ? JSON.parse(data) : null;
  }

  async set(key, data, ctx) {
    const ttl = ctx.revalidate || 3600;
    await client.setex(key, ttl, JSON.stringify({ value: data, lastModified: Date.now() }));
  }

  async revalidateTag(tag) {
    // Сканирование и удаление ключей с тегом
    const keys = await client.smembers(`tag:${tag}`);
    if (keys.length) await client.del(...keys);
    await client.del(`tag:${tag}`);
  }
};

Fallback-стратегии для новых страниц

Для маршрутов, не сгенерированных при сборке, три варианта поведения:

// Next.js: dynamicParams контролирует поведение
export const dynamicParams = true; // Генерировать on-demand (по умолчанию)
// export const dynamicParams = false; // 404 для несгенерированных путей

// Nuxt: routeRules в nuxt.config.ts
export default defineNuxtConfig({
  routeRules: {
    '/products/**': { isr: 300 },      // ISR с TTL 5 минут
    '/blog/**': { isr: true },          // ISR только on-demand
    '/dashboard/**': { ssr: true },     // Чистый SSR без кэша
    '/static/**': { prerender: true },  // Только SSG
  },
});

Мониторинг и отладка

Отслеживайте в production:

  • Cache HIT rate — отношение ответов из кэша к регенерациям
  • Revalidation duration — время фоновой регенерации (не должно превышать TTL)
  • Stale responses — количество ответов с устаревшим контентом
// middleware.ts — логирование cache status
import { NextResponse, type NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  const response = NextResponse.next();
  response.headers.set('x-cache-time', new Date().toISOString());
  return response;
}

Сроки реализации

  • Неделя 1–2: выбор стека, базовая SSG/SSR структура, TTL-стратегии по типам страниц
  • Неделя 3: on-demand revalidation, webhook-интеграция с CMS
  • Неделя 4: Redis cache handler для self-hosted, мониторинг cache hit rate
  • Неделя 5: нагрузочное тестирование, оптимизация fallback-страниц, документация для контент-команды
  • Неделя 6: деплой, настройка CI/CD с прогревом кэша после сборки