Розробка 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() });
}

Вебгачок з CMS викликає цей endpoint при публікації:

# Приклад запиту з Contentful вебгачка
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 Тільки при публікації

Кеш-сховище для розподіленого розгортання

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 — логування статусу кешу
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, інтеграція вебгачків з CMS
  • Тиждень 4: Redis cache handler для self-hosted, моніторинг cache hit rate
  • Тиждень 5: навантажувальне тестування, оптимізація fallback-сторінок, документація для контент-команди
  • Тиждень 6: розгортання, налаштування CI/CD з прогріванням кешу після збірки