W Metropolii Quantum roku 2150 wydajnosc systemow to kwestia przetrwania. Kazda milisekunda opoznienia moze oznaczac utrate danych w kwantowej sieci lub awarie krytycznych systemow. Dlatego inzynierowie Metropolii musza dokladnie rozumiec roznice wydajnosciowe miedzy strategiami renderowania w Next.js 15 i umiec wybrac optymalne rozwiazanie dla kazdego przypadku.
Benchmarki to pomiary wydajnosci aplikacji w kontrolowanych warunkach. Pozwalaja porownac rozne strategie renderowania pod katem kluczowych metryk, takich jak:
SSG generuje strony w czasie budowania aplikacji. Wynikowe pliki HTML sa serwowane bezposrednio z CDN, co zapewnia najnizsze mozliwe opoznienia.
1// app/blog/[slug]/page.tsx - SSG w Next.js 15
2import { notFound } from 'next/navigation';
3
4// Generowanie statycznych sciezek w czasie budowania
5export async function generateStaticParams() {
6 const posts = await fetch('https://api.example.com/posts').then(r => r.json());
7 return posts.map((post: { slug: string }) => ({ slug: post.slug }));
8}
9
10// Domyslne cachowanie = SSG
11async function getPost(slug: string) {
12 const res = await fetch(\`https://api.example.com/posts/\${slug}\`);
13 if (!res.ok) return null;
14 return res.json();
15}
16
17export default async function BlogPost({ params }: { params: Promise<{ slug: string }> }) {
18 const post = await getPost((await params).slug);
19 if (!post) notFound();
20
21 return (
22 <article>
23 <h1>{post.title}</h1>
24 <p>{post.content}</p>
25 </article>
26 );
27}Typowe metryki SSG:
| Metryka | Wartosc | Ocena | |---------|---------|-------| | TTFB | 5-50ms | Doskonala | | FCP | 100-300ms | Doskonala | | LCP | 200-500ms | Doskonala | | TTI | 300-800ms | Bardzo dobra | | CLS | 0-0.05 | Doskonala |
SSR renderuje strony na serwerze przy kazdym zadaniu. Jest wolniejsze niz SSG, ale zapewnia zawsze aktualne dane.
1// app/dashboard/page.tsx - SSR w Next.js 15
2import { cookies } from 'next/headers';
3
4// Wymuszenie dynamicznego renderowania
5export const dynamic = 'force-dynamic';
6
7async function getDashboardData(token: string) {
8 const res = await fetch('https://api.example.com/dashboard', {
9 headers: { Authorization: \`Bearer \${token}\` },
10 cache: 'no-store', // Brak cachowania = SSR
11 });
12 return res.json();
13}
14
15export default async function Dashboard() {
16 const cookieStore = await cookies();
17 const token = cookieStore.get('auth-token')?.value || '';
18 const data = await getDashboardData(token);
19
20 return (
21 <div>
22 <h1>Panel uzytkownika</h1>
23 <p>Saldo: {data.balance} QC</p>
24 <p>Ostatnia aktywnosc: {data.lastActivity}</p>
25 </div>
26 );
27}Typowe metryki SSR:
| Metryka | Wartosc | Ocena | |---------|---------|-------| | TTFB | 100-500ms | Srednia | | FCP | 300-800ms | Dobra | | LCP | 500-1200ms | Srednia | | TTI | 500-1500ms | Srednia | | CLS | 0-0.1 | Dobra |
ISR laczy zalety SSG (szybkosc) z SSR (aktualnosc danych). Strony sa generowane statycznie, ale odswiezane w tle po uplywie okreslonego czasu.
1// app/products/page.tsx - ISR w Next.js 15
2async function getProducts() {
3 const res = await fetch('https://api.example.com/products', {
4 next: { revalidate: 60 }, // Rewalidacja co 60 sekund
5 });
6 return res.json();
7}
8
9export default async function ProductsPage() {
10 const products = await getProducts();
11
12 return (
13 <div>
14 <h1>Produkty Quantum Store</h1>
15 <div className="grid">
16 {products.map((product: any) => (
17 <div key={product.id}>
18 <h2>{product.name}</h2>
19 <p>Cena: {product.price} QC</p>
20 </div>
21 ))}
22 </div>
23 </div>
24 );
25}Typowe metryki ISR:
| Metryka | Wartosc (cache hit) | Wartosc (rewalidacja) | Ocena | |---------|--------------------|-----------------------|-------| | TTFB | 5-50ms | 100-400ms | Bardzo dobra | | FCP | 100-300ms | 300-700ms | Bardzo dobra | | LCP | 200-500ms | 500-1000ms | Dobra | | TTI | 300-800ms | 500-1200ms | Dobra | | CLS | 0-0.05 | 0-0.05 | Doskonala |
Next.js 15 wprowadza udoskonalona rewalidacje na zadanie, ktora pozwala odswiezyc cache bez czekania na uplyniecie czasu rewalidacji:
1// app/api/revalidate/route.ts
2import { revalidatePath, revalidateTag } from 'next/cache';
3import { NextRequest, NextResponse } from 'next/server';
4
5export async function POST(request: NextRequest) {
6 const { path, tag, secret } = await request.json();
7
8 // Weryfikacja tokenu bezpieczenstwa
9 if (secret !== process.env.REVALIDATION_SECRET) {
10 return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
11 }
12
13 if (tag) {
14 // Rewalidacja po tagu - odswierza wszystkie fetche z danym tagiem
15 revalidateTag(tag);
16 return NextResponse.json({ revalidated: true, tag });
17 }
18
19 if (path) {
20 // Rewalidacja sciezki - odswierza konkretna strone
21 revalidatePath(path);
22 return NextResponse.json({ revalidated: true, path });
23 }
24
25 return NextResponse.json({ error: 'Missing path or tag' }, { status: 400 });
26}
27
28// Uzycie tagow w fetch
29async function getProduct(id: string) {
30 const res = await fetch(\`https://api.example.com/products/\${id}\`, {
31 next: {
32 revalidate: 3600, // Cache na 1 godzine
33 tags: [\`product-\${id}\`, 'products'], // Tagi do rewalidacji
34 },
35 });
36 return res.json();
37}W Next.js 15 mozna laczyc rozne strategie renderowania w ramach jednej strony dzieki Partial Prerendering (PPR):
1// app/store/page.tsx - Hybrid approach
2import { Suspense } from 'react';
3
4// Statyczna czesc strony (SSG)
5function StaticHeader() {
6 return (
7 <header>
8 <h1>Quantum Store</h1>
9 <nav>
10 <a href="/products">Produkty</a>
11 <a href="/categories">Kategorie</a>
12 </nav>
13 </header>
14 );
15}
16
17// Dynamiczna czesc strony (SSR)
18async function DynamicCart() {
19 const res = await fetch('https://api.example.com/cart', { cache: 'no-store' });
20 const cart = await res.json();
21
22 return (
23 <div>
24 <h2>Twoj koszyk ({cart.items.length} produktow)</h2>
25 <p>Suma: {cart.total} QC</p>
26 </div>
27 );
28}
29
30// Strona ISR z dynamicznymi elementami
31async function ProductList() {
32 const res = await fetch('https://api.example.com/featured', {
33 next: { revalidate: 300 },
34 });
35 const products = await res.json();
36
37 return (
38 <div>
39 {products.map((p: any) => (
40 <div key={p.id}>{p.name} - {p.price} QC</div>
41 ))}
42 </div>
43 );
44}
45
46export default function StorePage() {
47 return (
48 <div>
49 <StaticHeader />
50 <Suspense fallback={<p>Ladowanie koszyka...</p>}>
51 <DynamicCart />
52 </Suspense>
53 <Suspense fallback={<p>Ladowanie produktow...</p>}>
54 <ProductList />
55 </Suspense>
56 </div>
57 );
58}1// app/layout.tsx
2import { SpeedInsights } from '@vercel/speed-insights/next';
3import { Analytics } from '@vercel/analytics/react';
4
5export default function RootLayout({ children }: { children: React.ReactNode }) {
6 return (
7 <html lang="pl">
8 <body>
9 {children}
10 <SpeedInsights />
11 <Analytics />
12 </body>
13 </html>
14 );
15}1# Budowanie z profilowaniem
2NEXT_TELEMETRY_DEBUG=1 next build
3
4# Analiza rozmiaru bundla
5ANALYZE=true next build| Scenariusz | Strategia | Dlaczego? | |------------|-----------|-----------| | Blog, dokumentacja | SSG | Tresci sie nie zmieniaja czesto | | E-commerce (lista produktow) | ISR (60-300s) | Produkty sie zmieniaja, ale nie co sekunde | | Dashboard uzytkownika | SSR | Dane musza byc zawsze aktualne | | Strona glowna z promocjami | ISR (300-3600s) | Promocje zmieniaja sie co kilka godzin | | Panel administracyjny | SSR | Wymaga aktualnych danych i autoryzacji | | Landing page | SSG | Maksymalna szybkosc, statyczna tresc | | Feed spolecznosciowy | SSR + Streaming | Dynamiczne dane, progresywne ladowanie |
Wybor strategii renderowania w Next.js 15 powinien byc podyktowany konkretnymi wymaganiami projektu:
W Metropolii Quantum 2150 inzynierowie opieraja swoje decyzje na danych - mierza, analizuja i optymalizuja. Benchmarki wydajnosci sa kluczowym narzedziem w tym procesie.