Были ли проблемы с кэшированием у приложения с Next.js
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Проблемы с кэшированием в Next.js
Кэширование в Next.js — это мощный инструмент для производительности, но он может быть источником серьёзных проблем, если не понимать как он работает. На собеседовании этот вопрос показывает, есть ли опыт работы с production-приложениями.
Основные типы кэширования в Next.js
Next.js имеет несколько слоёв кэширования:
1. Static Generation (SSG) — на build time
2. Incremental Static Regeneration (ISR) — с revalidate
3. Server-Side Rendering (SSR) — не кэшируется
4. Client-side кэширование (fetch, SWR, React Query)
5. CDN кэширование (Vercel, Cloudflare)
Проблема 1: Устаревший контент (Stale Content)
Вероятно самая частая проблема:
// pages/products/[id].js
export async function getStaticProps({ params }) {
const product = await fetch(`/api/products/${params.id}`).then(r => r.json());
return {
props: { product },
revalidate: 60 // перегенерировать каждую минуту
};
}
// Проблема: если товар изменился, пользователь увидит старые данные в течение 60 секунд
// Критично для e-commerce, где цены и наличие меняются часто
Решение — ISR с более правильными параметрами:
export async function getStaticProps({ params }) {
const product = await fetchProduct(params.id);
return {
props: { product },
revalidate: process.env.NODE_ENV === 'production' ? 10 : false
// В продакшене перегенерируем каждые 10 секунд
// В разработке — не кэшируем
};
}
Проблема 2: Cache не инвалидируется при обновлении данных
Сценарий: пользователь обновил товар, но видит старую версию
// pages/api/products/[id]/update.js
export default async function handler(req, res) {
if (req.method === 'PUT') {
const updated = await updateProduct(req.body);
// ❌ Неправильно: не инвалидируем кэш
res.json(updated);
// ✅ Правильно: инвалидируем кэш
// Используем On-Demand ISR (Next.js 12.2+)
await res.revalidate(`/products/${req.body.id}`);
res.json(updated);
}
}
Проблема 3: Cache на фронтенде (fetch кэширование)
Next.js 13+ автоматически кэширует fetch запросы (Data Cache):
// app/products/[id]/page.js
async function getProduct(id) {
const res = await fetch(`/api/products/${id}`);
// ⚠️ По умолчанию это кэшируется НАВСЕГДА!
return res.json();
}
// ✅ Правильно: явно указать время кэша
async function getProduct(id) {
const res = await fetch(`/api/products/${id}`, {
next: { revalidate: 60 } // перегенерировать каждую минуту
});
return res.json();
}
// ✅ Правильно: не кэшировать
async function getProduct(id) {
const res = await fetch(`/api/products/${id}`, {
cache: 'no-store' // всегда свежо
});
return res.json();
}
Проблема 4: Несоответствие между SSG и SSR
Смешивание SSG и SSR может привести к проблемам:
// ❌ Неправильно: гибридный подход
export async function getStaticProps() {
// Может быть медленно, если много страниц
const products = await fetchAllProducts();
return { props: { products }, revalidate: 60 };
}
// ✅ Правильно: понимать trade-offs
// Для часто изменяемых данных: SSR
export async function getServerSideProps() {
const products = await fetchAllProducts();
return { props: { products } };
}
// Для статичных данных: SSG
export async function getStaticProps() {
const products = await fetchAllProducts();
return { props: { products }, revalidate: 86400 }; // раз в день
}
Проблема 5: Browser Cache Headers
Неправильные headers могут привести к проблемам на фронтенде:
// pages/api/products/[id].js
export default function handler(req, res) {
// ❌ Неправильно: браузер кэширует API ответ
const product = getProduct(req.query.id);
res.json(product);
// ✅ Правильно: явно указать кэширование
res.setHeader('Cache-Control', 'public, max-age=60, stale-while-revalidate=120');
res.json(product);
// ✅ Для приватных данных
res.setHeader('Cache-Control', 'private, max-age=60');
res.json(user);
// ✅ Для данных, которые не должны кэшироваться
res.setHeader('Cache-Control', 'no-store');
res.json(sensitiveData);
}
Проблема 6: CDN кэширование конфликтует с ISR
Если используешь Cloudflare или другой CDN:
// ❌ Проблема: CDN кэширует HTML, а ISR хочет обновить
// На Cloudflare нужно настроить корректно
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
onDemandEntries: {
maxInactiveAge: 60 * 1000, // 1 минута
pagesBufferLength: 5,
},
};
// Плюс в Cloudflare (если используешь):
// Cache Rule: HTML кэшируется на 1 минуту, а не навсегда
Проблема 7: Проблемы с preview mode
Забывают выключить preview mode в production:
// pages/api/preview.js
export default function handler(req, res) {
// ❌ Опасно: любой может перейти в preview mode
if (req.query.secret === 'PREVIEW_SECRET') {
res.setPreviewData({});
res.redirect('/');
}
// ✅ Правильно: проверить токен
if (req.query.secret !== process.env.PREVIEW_SECRET) {
return res.status(401).json({ message: 'Unauthorized' });
}
res.setPreviewData({});
res.redirect('/');
}
Реальный пример: e-commerce
Мой опыт с проблемой кэширования:
// Ситуация: продаём товары, цена меняется
// Проблема: кэш показал старую цену, потеря продаж
// Решение
export async function getStaticProps({ params }) {
const product = await db.products.findById(params.id);
return {
props: { product },
// Критично: перегенерировать каждую минуту
revalidate: 60,
// На продакшене используем стабильный host
};
}
// Плюс: на-demand инвалидация при обновлении
export default function handler(req, res) {
if (req.method === 'PUT') {
const updated = updateProduct(req.body);
// Инвалидируем кэш
res.unstable_revalidatePath(`/products/${updated.id}`);
res.json(updated);
}
}
Debugging кэша
// Как проверить кэширование?
// 1. Network tab в DevTools
// Смотрим Status:
// - 200 от сервера
// - 304 Not Modified (браузер кэш)
// - 200 от CDN (X-Cache: HIT)
// 2. Логирование
export async function getStaticProps() {
console.log('getStaticProps called at', new Date());
// Вызывается только на build time и при revalidate
}
// 3. Headers
// curl -I https://example.com/products/1
// Смотрим: Cache-Control, ETag, Last-Modified
Best Practices
// 1. Для данных, которые редко меняются (раз в день)
export async function getStaticProps() {
return { props, revalidate: 86400 };
}
// 2. Для данных, которые часто меняются (каждую минуту)
export async function getServerSideProps() {
return { props };
}
// 3. Для данных, которые очень часто меняются
// Использовать client-side fetching с SWR или React Query
function Component() {
const { data } = useSWR('/api/live-data', fetcher, {
revalidateOnFocus: true
});
}
// 4. На-demand инвалидация
res.unstable_revalidatePath('/products');
res.unstable_revalidatePath('/products/[id]');
res.unstable_revalidatePath('/'); // весь сайт (осторожно!)
На собеседовании
Краткий ответ: Да, встречался с проблемами кэширования. Самая частая — забыли установить revalidate для ISR, и пользователи видят устаревший контент. Также проблемы с fetch кэшированием в App Router, где по умолчанию кэшируется навсегда.
Развёрнутый ответ: Встречался с несколькими типами проблем: 1) ISR без правильного revalidate показывает старый контент; 2) fetch кэширование в App Router кэширует навсегда (нужно cache: no-store); 3) Забывают инвалидировать кэш при обновлении данных (нужно res.revalidatePath); 4) Конфликты с CDN кэшированием (нужны правильные Cache-Control headers); 5) Preview mode оставляют включённым. Решение — использовать ISR для статичных данных, SSR для часто меняющихся, client-side fetching для real-time, и всегда думать о trade-offs между свежестью данных и производительностью.