Оптимизация продакшена
Next.js даёт много инструментов. Но нужны правильные настройки.
Bundle size
Проверь:
ANALYZE=true npm run build
Что делать:
- Динамические импорты для тяжёлых компонентов
optimizePackageImportsдля библиотек иконок- Заменить тяжёлые зависимости на лёгкие
const Chart = dynamic(() => import('@/components/Chart'), {
loading: () => <Skeleton />,
ssr: false,
});
const nextConfig = {
experimental: {
optimizePackageImports: ['lucide-react', 'date-fns'],
},
};
Изображения
Используй next/image:
import Image from 'next/image';
<Image
src="/hero.jpg"
alt="Hero"
width={1200}
height={600}
priority // для первого экрана
sizes="(max-width: 768px) 100vw, 1200px"
/>
Плюсы:
- Конвертация в WebP/AVIF
- Ресайз под экран
- Ленивая загрузка
- Предотвращение layout shift
Шрифты
import { Inter } from 'next/font/google';
const inter = Inter({ subsets: ['latin', 'cyrillic'], display: 'swap' });
Self-hosting, без запросов к Google.
Static vs Dynamic
Статика быстрее. Проверь, какие страницы можно сделать статическими:
export const dynamic = 'force-static';
Или ISR:
export const revalidate = 3600;
Кеш fetch
// Динамические данные
fetch(url, { cache: 'no-store' });
// Статика
fetch(url, { cache: 'force-cache' });
// ISR
fetch(url, { next: { revalidate: 60 } });
CDN
Vercel кеширует статику автоматически. На своём сервере — настрой CDN:
- Cloudflare — бесплатно
- Fastly, Bunny — для больших проектов
Кешируй:
/_next/static/*— навсегда/public/*— на день-месяц- HTML — коротко, по стратегии
Сжатие
Vercel использует Brotli автоматически. На своём сервере:
gzip on;
gzip_types text/plain text/css application/json application/javascript;
brotli on;
brotli_types text/plain text/css application/json application/javascript;
Кеш в Nginx
location /_next/static/ {
proxy_pass http://localhost:3000;
proxy_cache_valid 200 365d;
add_header Cache-Control "public, max-age=31536000, immutable";
}
location /public/ {
proxy_pass http://localhost:3000;
proxy_cache_valid 200 30d;
}
Preload критичных ресурсов
<Head>
<link rel="preload" href="/fonts/inter.woff2" as="font" type="font/woff2" crossOrigin="anonymous" />
</Head>
Suspense и Streaming
Медленные части — в <Suspense>:
<Suspense fallback={<Skeleton />}>
<SlowComponent />
</Suspense>
Пользователь видит оболочку сразу.
Core Web Vitals
- LCP — самый большой контент. Оптимизируй
Imageсpriority. - INP — отклик на действие. Убирай блокирующий JS.
- CLS — сдвиг layout. Задавай размеры картинок, шрифты с
display: swap.
Проверь:
- PageSpeed Insights
- Lighthouse
- Vercel Speed Insights
Кеш данных
Кешируй тяжёлые запросы:
import { unstable_cache } from 'next/cache';
export const getPosts = unstable_cache(
async () => db.post.findMany(),
['posts'],
{ revalidate: 60, tags: ['posts'] }
);
Логи и метрики
- Sentry — ошибки
- Vercel Analytics — посещаемость
- Speed Insights — Core Web Vitals
- Axiom, Logtail — логи
Database connection pooling
Serverless-функции создают много соединений. Решения:
- Prisma Accelerate
- PgBouncer
- Neon, Supabase — serverless-базы
DATABASE_URL=postgresql://...?pgbouncer=true&connection_limit=1
Итоги
- Анализ бандла
next/imageиnext/font- Статика где можно
- CDN и кеш
- Сжатие Brotli
- Suspense и Streaming
- Core Web Vitals
- Connection pooling