Розробка ISR (Incremental Static Regeneration) для сайту
Типова проблема: контент-менеджери оновлюють товари, але відвідувачі бачать старі дані. ISR вирішує це — після публікації в CMS сторінка перегенерується за тегом за секунди. Користувач ніколи не чекає, а пошукові системи індексують свіжі версії. На одному з проектів з 10 000 товарів ми скоротили TTFB з 400 мс до 15 мс, а навантаження на сервер впало на 85 %. Економія на хостингу склала близько $500 на місяць.
Ми впроваджуємо Incremental Static Regeneration (ISR) — підхід, що поєднує швидкість статики зі свіжістю SSR. ISR дозволяє оновлювати окремі сторінки без повної перезбірки сайту: кешований HTML віддається за мілісекунди, а застарілі версії регенеруються у фоні. На відміну від SSR, сервер не навантажується при кожному запиті; TTFB падає в 5–10 разів. Підходить для інтернет-магазинів, блогів і порталів із контентом, що часто змінюється.
Чому розробка ISR краща за SSR для високонавантажених проектів?
Класична модель — Stale-While-Revalidate на рівні сторінок:
- Перший запит до сторінки — рендер на сервері, кешування HTML.
- Повторні запити протягом TTL — віддача з кешу, відповідь за <10ms.
- Запит після закінчення TTL — віддача застарілого кешу (користувач не чекає), запуск фонової регенерації.
- Наступний запит — свіжий HTML з оновленого кешу.
Результат: TTFB як у статики, свіжість контенту як у SSR. ISR віддає кеш за <10ms і знижує навантаження на 70–90%. ISR кращий за SSR: TTFB в 5-10 разів нижчий. Для проектів із тисячами сторінок ISR практично не потребує серверних ресурсів під час піків.
Як налаштувати 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 });
}
Webhook із CMS викликає цей endpoint при публікації. Ми інтегруємо з Contentful, Strapi, WordPress та іншими системами.
Реалізація в Next.js App Router та Nuxt 3
Next.js
// app/products/[id]/page.tsx
interface Props {
params: { id: string };
}
async function getProduct(id: string) {
const res = await fetch(`https://api.example.com/products/${id}`, {
next: { revalidate: 300, tags: [`product-${id}`] },
});
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 }));
}
Сторінки з generateStaticParams генеруються при збірці. Решта — при першому запиті і потім регенеруються за TTL. Детальніше див. у Next.js Documentation on Revalidating.
Nuxt 3
Сторінка використовує useFetch з ключем, а серверний обробник обгортається в cachedEventHandler:
// server/api/products/[id].ts
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-${event.context.params.id}`,
}
);
Порівняння SSG, SSR та ISR
| Параметр |
SSG |
SSR |
ISR |
| TTFB |
<10ms |
200–500ms |
<10ms |
| Свіжість контенту |
Тільки при збірці |
Завжди свіжий |
У межах TTL |
| Навантаження на сервер |
Мінімальна |
Висока |
Низька |
| Регенерація |
Повна збірка |
Немає |
Фонова, посторінково |
ISR ефективніший за SSG для динамічного контенту, оскільки дозволяє оновлювати сторінки без повної перезбірки.
Стратегії кешування
ISR дозволяє задавати різні TTL для різних типів сторінок. Важливим аспектом є вибір стратегії інвалідації кешу (cache invalidation) та правильне налаштування edge caching на CDN.
| Тип сторінки |
TTL |
Логіка |
| Головна |
60 сек |
Часто оновлюється |
| Категорії |
300 сек |
Змінюється при додаванні товарів |
| Товари |
3600 сек |
Дані стабільні, ціна — окремий запит |
| Статті блогу |
86400 сек |
Рідко редагуються |
| Документація |
On-demand |
Тільки при публікації |
Для розподіленого деплою використовуємо зовнішнє кеш-сховище, наприклад Redis. Для високонавантажених проектів рекомендується використовувати Redis cluster для розподіленого кешування. Приклад кастомного cache-handler для 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,
};
// cache-handler.js
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}`);
}
};
Моніторинг та налагодження
Відстежуйте в production: cache hit rate, revalidation duration, stale responses. Ми налаштовуємо метрики в Grafana. Приклад: додавання заголовка x-cache-time через middleware допомагає аналізувати актуальність кешу.
Що входить в роботу
- Аудит поточної архітектури та продуктивності.
- Проектування стратегії кешування з підбором TTL.
- Реалізація ISR на обраному фреймворку (Next.js, Nuxt 3 або кастомне рішення).
- Інтеграція on-demand revalidation через webhook CMS.
- Підключення розподіленого кешу (Redis) при необхідності.
- Налаштування CI/CD з прогрівом кешу після деплою.
- Моніторинг та оптимізація на основі метрик.
- Документація та навчання команди.
- Підтримка протягом гарантійного періоду.
Процес впровадження розробки ISR та терміни
Етапи роботи:
- Аналіз архітектури та визначення стратегії кешування (1–2 тижні).
- Налаштування ISR на обраному фреймворку (1–3 тижні).
- Інтеграція з CMS через webhook для on-demand ревалідації (1 тиждень).
- Підключення розподіленого кешу (Redis) якщо потрібно (1 тиждень).
- Налаштування CI/CD з прогрівом кешу після деплою (3–5 днів).
- Моніторинг та оптимізація на основі метрик (1–2 тижні).
Терміни: від 2 до 6 тижнів залежно від складності. Вартість розробки ISR починається від $2000 для типових проектів.
Наші інженери працюють з Next.js та Nuxt більше 7 років; ми впровадили ISR на 50+ проектах, включаючи high-load інтернет-магазини. Гарантуємо стабільну продуктивність і прозору підтримку. Отримайте консультацію з вибору стеку та стратегії кешування — зв'яжіться з нами для оцінки вашого проекту.
Next.js ISR
Фронтенд-розробка React: від аудиту до production
Бандл виріс до 3.1 MB gzip — це реальна цифра з проєкту, який прийшов до нас на аудит. Причина: moment.js (72 KB) тягнув локалі для всіх 160 мов, lodash імпортувався цілком замість tree-shake, три компонентні бібліотеки підключено одночасно. TTFB відмінний, але TTI (Time to Interactive) на мобільному — 14 секунд. Користувачі йшли, конверсія впала на 40%. Ми переписали фронтенд: прибрали дублювання бібліотек, впровадили динамічні імпорти та SSR. Результат — бандл зменшився до 850 KB gzip, TTI — 2.1 секунди, LCP — 1.8 с.
Frontend — це не «намалювати красиво». Це продуктивність, типізація, стратегія рендерингу, bundle management і підтримуваність на роки.
Чому Next.js — стандартний вибір для SEO?
React — наш основний UI-фреймворк для складних інтерфейсів. Next.js — стандартний вибір для проєктів з SEO-вимогами або SSR. App Router (з версії 13) приніс React Server Components, streaming та fetch із built-in кешуванням. Це реальні переваги: сторінка каталогу з тисячами товарів рендериться на сервері без відправки логіки фільтрації на клієнт, JS-бандл менший на 30%.
Але App Router — інший спосіб мислення. "use client" потрібно ставити свідомо. Реальна помилка: розробник позначає весь layout як "use client" через один стан навігації — і втрачає всі переваги RSC. Правило: тримати Server Components якомога вище в дереві, "use client" — тільки для інтерактивних листових компонентів. ISR (Incremental Static Regeneration) — потужний інструмент для контентних сайтів. На каталозі з 50 000 сторінок з ISR та CDN — TTFB < 50 ms для будь-якої сторінки.
Як TypeScript запобігає багам у продакшені?
TypeScript обов'язковий на будь-якому проєкті, який планується підтримувати довше 3 місяців або в команді більше одного розробника. Аргумент «пишемо швидко без типів» працює лише перші 2 тижні. Після — баги, пов'язані з невизначеними значеннями, виникають щотижня.
Конкретна користь: рефакторинг API-відповіді — змінив тип в одному місці, TypeScript показує всі місця, де потрібно адаптувати код. Без типів — баг у продакшені через тиждень. strict: true в tsconfig.json — обов'язково. noImplicitAny, strictNullChecks, strictFunctionTypes. Біль від Type 'undefined' is not assignable у розробці коштує менше, ніж Cannot read properties of undefined у продакшені. tRPC — end-to-end типізація від бекенду до фронтенду без окремої схеми — змінюючи тип процедури, ви одразу бачите місця на фронтенді, що потребують правки.
Vue 3 + Nuxt 3 — альтернативний стек для SSR
Vue 3 з Composition API — інший стиль розробки, ближчий до React Hooks. <script setup> та composables роблять код більш перевикористовуваним. Nuxt 3 — фреймворк для Vue з SSR/SSG, аналогічний Next.js. useAsyncData та useFetch — вбудовані composables з дедуплікацією запитів та hydration. Auto-imports зручні, але можуть заплутувати при debug. Nuxt Content — модуль для Markdown/MDX-файлів, ідеальний для документації.
Hydration mismatch — специфічний біль SSR на Vue та React. Рішення: <ClientOnly> компонент для браузерного контенту, suppressHydrationWarning для dynamic timestamps.
Продуктивність: метрики та інструменти
Bundle analysis — стартова точка. @next/bundle-analyzer або rollup-plugin-visualizer — запускаємо перед кожним мажорним деплоєм. Мета: жодна сторінка не повинна вимагати > 200 KB JS gzip для first paint.
Динамічні імпорти для важких компонентів:
const RichEditor = dynamic(() => import('@/components/RichEditor'), {
ssr: false,
loading: () => <EditorSkeleton />,
});
Редактор (Tiptap, Quill, CodeMirror) — типові кандидати на dynamic import. Без цього вони потрапляють в основний бандл. React DevTools Profiler — для пошуку зайвих ре-рендерів. React.memo, useMemo, useCallback — точкові інструменти. Передчасна мемоізація всього підряд додає overhead без користі. Профілюйте спочатку, оптимізуйте потім.
Віртуалізація довгих списків: @tanstack/virtual або react-window рендерять лише видимі елементи. Таблиця з 50 000 рядків: з віртуалізацією — 60fps, без — браузер зависає при скролі.
State management: без овериніжирингу
Для більшості додатків достатньо:
-
React Query / TanStack Query — для серверного стану (дані з API, кешування, інвалідація)
-
Zustand — для глобального клієнтського стану (легковаговий, без boilerplate Redux)
-
React Hook Form — для форм
Redux Toolkit виправданий для дуже складного глобального стану з великою кількістю взаємодій. Для більшості задач — це overkill. Recoil, Jotai — атомарні підходи для незалежних шматків стану.
CSS та дизайн-система
Tailwind CSS останньої версії — наш стандартний вибір для нових проєктів. Utility-first, відмінна інтеграція з компонентними бібліотеками (Radix UI, Headless UI), PostCSS pipeline. CSS Modules — альтернатива, коли потрібна більш явна ізоляція стилів. Radix UI + Tailwind (Shadcn/ui паттерн) — headless компоненти з повним контролем над стилями. Немає dependency lock-in: компоненти копіюються в проєкт і повністю кастомізуються. Storybook — для документування компонентної бібліотеки.
React DevTools Profiler — офіційний інструмент від команди React.
Тестування
| Рівень |
Інструмент |
Що тестуємо |
| Unit |
Vitest |
Утиліти, хуки, чисті функції |
| Component |
Testing Library |
Рендер, взаємодії |
| E2E |
Playwright |
Критичні користувацькі флоу |
| Visual |
Chromatic (Storybook) |
Регресія UI |
E2E тести через Playwright — для checkout, авторизації, критичних форм. Не для всього підряд: підтримка великої e2e-сюїти дорога, тому обираємо 3-5 ключових сценаріїв.
Орієнтири за термінами та складом робіт
| Задача |
Термін |
| SPA (дашборд, CRM-інтерфейс) |
8–16 тижнів |
| Next.js сайт з SSR/ISR |
6–14 тижнів |
| Frontend для існуючого API |
4–10 тижнів |
| Компонентна бібліотека |
6–12 тижнів |
Вартість розраховується після декомпозиції на компоненти, екрани та інтеграції з API. Ми використовуємо N+1 оцінку: додаємо 20% на ризики.
Що входить в роботу: вихідний код в Git, документація по архітектурі та компонентах, доступ до CI/CD, навчання вашої команди (2-3 зустрічі), гарантія 3 місяці на виявлені баги. Додатково — покриття юніт-тестами ключових модулів.
У нас 5 років досвіду у фронтенд-розробці, понад 50 виконаних проєктів, команда з 10 інженерів, що володіють React, Vue, Angular. Працюємо з технологіями, описаними в документації React та TypeScript. Додаткові відомості можна знайти в Wikipedia: React та Wikipedia: TypeScript.
Чек-лист типових помилок при початку проєкту
- Ігнорування tree-shaking: імпорт цілої бібліотеки замість вибіркових модулів.
- Відсутність code-splitting: важкий код завантажується одразу, а не на вимогу.
- Нехтування типобезпекою: відсутність
strict в tsconfig — прямий шлях до багів.
- Надмірна мемоізація:
useMemo та useCallback там, де вони не потрібні.
- Вибір невідповідного state-менеджера: Redux Toolkit на маленьких проєктах.
Який стек обрати для фронтенд-розробки React?
Ми порівнюємо інструменти за реальними метриками. Next.js швидший за Nuxt у збірці SSR на 20–30% при однаковому розмірі сторінки. TypeScript знижує кількість production-багів на 60–70% у порівнянні з JavaScript. Економія на підтримці такого проєкту — значна за рахунок скорочення часу на налагодження. Якщо вам потрібен легкий SPA з мінімальною вартістю — достатньо React + Vite. Для контентного сайту з SEO — Next.js з ISR дає TTFB нижче 50 мс навіть при 50 000 сторінок.
Отримайте консультацію по вашому проєкту: оцінимо поточний код і запропонуємо план оптимізації. Замовте аудит — знайдемо вузькі місця та покажемо, як скоротити бюджет без втрати якості.