Next.js static site generation (SSG) ir SSR

Kai reikia rinktis tarp dviejų pasaulių

Prisimenu, kai pirmą kartą susidūriau su Next.js – buvau įpratęs prie klasikinio React SPA, kur viskas vyksta kliento pusėje. Tada atėjo projektas, kuriame SEO buvo kritiškai svarbus, ir pradėjau gilintis į serverio pusės renderinimą. Žinoma, kaip ir daugelis, iškart susidūriau su klausimu: SSG ar SSR? Atrodė, kad abu daro beveik tą patį, bet praktikoje skirtumas yra milžiniškas.

Next.js suteikia galimybę rinktis ne tik tarp šių dviejų metodų, bet ir juos kombinuoti viename projekte. Tai vienas iš pagrindinių framework’o privalumų – lankstumas. Tačiau būtent dėl šio lankstumo daugelis ir paklysta. Matau tai code review metu nuolat – žmonės pasirenka SSR, nors puikiai užtektų SSG, arba atvirkščiai.

Kas iš tikrųjų vyksta po gaubtu

SSG (Static Site Generation) generuoja HTML failus build metu. Tai reiškia, kad kai paleidžiate next build, Next.js sukuria visus puslapius kaip statinius HTML failus. Kai vartotojas ateina į jūsų svetainę, jis gauna jau paruoštą HTML – be jokių papildomų užklausų į serverį ar duomenų bazę.

SSR (Server-Side Rendering) veikia kitaip. Kiekvieną kartą, kai kas nors užklausia puslapį, serveris generuoja HTML tuo momentu. Tai reiškia, kad serveris turi būti gyvas, veikiantis ir pasiruošęs apdoroti kiekvieną request’ą. Duomenys gali būti gaunami iš duomenų bazės, API ar bet kokio kito šaltinio realiu laiku.

Skamba paprasta, bet čia ir prasideda įdomybės. SSG su getStaticProps gali naudoti ISR (Incremental Static Regeneration), kas leidžia atnaujinti statinius puslapius fone, neperkompiliuojant viso projekto. O SSR su getServerSideProps gali cache’inti rezultatus, kas daro jį panašesnį į SSG, bet su dinamiškumu.

Kada SSG yra akivaizdus pasirinkimas

Turiu klientą, kuris valdo technologijų naujienų portalą. Straipsniai keičiasi retai – gal keletas naujų per dieną. Čia SSG yra idealus. Build’inam puslapius kas valandą ar kas kelias valandas su ISR, ir vartotojai gauna žaibiškai greitą patirtį. CDN gali cache’inti šiuos failus visame pasaulyje, o serverio apkrova yra minimali.

SSG puikiai tinka:
– Dokumentacijai ir žinių bazėms
– Tinklaraščiams ir naujienų portalams
– Marketing puslapiams ir landing pages
– Portfolio svetainėms
– E-komercijos produktų katalogas (su ISR)

Praktiškai, jei jūsų duomenys nesikeičia kas sekundę ir nėra labai personalizuoti, SSG turėtų būti jūsų default pasirinkimas. Greitis yra neįtikėtinas – mes kalbame apie <50ms atsakymo laikus, nes tai tiesiog statiniai failai. Štai kaip atrodo tipinis SSG implementavimas:
export async function getStaticProps() {
const posts = await fetchPosts();

return {
props: { posts },
revalidate: 3600 // ISR: atnaujinti kas valandą
};
}

ISR čia yra game-changer. Galite nustatyti revalidate reikšmę sekundėmis, ir Next.js automatiškai atnaujins puslapį fone, kai pasibaigs šis laikas. Pirmasis vartotojas po revalidation periodo gaus seną versiją, bet fone jau generuojama nauja. Kiti vartotojai gaus atnaujintą versiją.

SSR situacijos, kai alternatyvos nėra

Dirbu su fintech startupu, kur vartotojo dashboard’as rodo realaus laiko finansinius duomenis. Čia SSG neveiks – duomenys turi būti šviežiausi, personalizuoti kiekvienam vartotojui. SSR tampa būtinybe.

SSR būtinas, kai:
– Duomenys yra labai dinaminiai ir keičiasi nuolat
– Turinys yra giliai personalizuotas pagal vartotoją
– Reikia prieigos prie request headers (cookies, auth tokens)
– Naudojate A/B testavimą ar feature flags
– Duomenys priklauso nuo query parametrų, kurių negalite numatyti

Tačiau SSR turi kainą. Kiekvienas request’as reiškia serverio darbą, duomenų bazės užklausas, galimus bottleneck’us. Matau projektus, kur SSR naudojamas be cache’inimo strategijos, ir serveriai tiesiog miršta po kiekvieno traffic spike’o.


export async function getServerSideProps(context) {
const { req, query } = context;
const userId = req.cookies.userId;

const userData = await fetchUserData(userId);
const filteredData = await filterByQuery(query);

return {
props: { userData, filteredData }
};
}

Svarbu suprasti, kad getServerSideProps vykdomas kiekviename request’e. Jei jūsų duomenų gavimas užtrunka 500ms, vartotojas lauks tiek laiko prieš matydamas bet ką. Čia optimizacija tampa kritine – database indexai, query optimizavimas, caching sluoksniai.

Hibridiniai sprendimai ir realaus pasaulio scenarijai

Daugelis projektų nėra juodi ar balti. Turėjau e-komercijos projektą, kur produktų sąrašas buvo SSG su ISR (produktai keičiasi retai), bet checkout procesas buvo SSR (realaus laiko inventoriaus patikra, personalizuoti pasiūlymai).

Next.js leidžia maišyti metodus tame pačiame projekte. Galite turėti:
/blog/[slug] su SSG
/dashboard su SSR
/api/* su API routes

Vienas iš mano mėgstamiausių pattern’ų yra „SSG su client-side data fetching”. Generuojate pagrindinį puslapio skeleton’ą su SSG (greitas initial load, geras SEO), o tada client’e fetch’inate dinaminius duomenis. Tai suteikia gerą balansą tarp greičio ir dinamiškumo.


// SSG pagrindiniam turiniui
export async function getStaticProps() {
const staticContent = await fetchStaticContent();
return { props: { staticContent } };
}

// Client-side dinaminiams duomenims
function ProductPage({ staticContent }) {
const { data: liveData } = useSWR(‘/api/live-data’, fetcher);

return (
<>

{liveData && }
</>
);
}

Performance implikacijos, apie kurias niekas nekalba

Teorijoje SSG visada greitesnis. Praktikoje – ne visada. Jei jūsų SSG puslapis yra 5MB JavaScript bundle’as, o SSR puslapis yra optimizuotas ir siunčia minimalų JS, SSR gali jaustis greičiau.

Matau dažną klaidą: žmonės mano, kad SSG automatiškai reiškia greitą svetainę. Bet jei jūsų React komponentai yra perkrauti, jei naudojate sunkias bibliotekas, jei neoptimizuojate images – SSG neišgelbės. First Contentful Paint gali būti greitas (nes HTML jau ten), bet Time to Interactive gali būti siaubingas.

SSR turi kitokią problemą – TTFB (Time to First Byte). Jei serveris yra lėtas ar geografiškai toli, vartotojas lauks. Čia edge functions tampa įdomūs – Vercel Edge Functions ar Cloudflare Workers leidžia vykdyti SSR arčiau vartotojo. Bet tai prideda sudėtingumo ir apribojimų.

Realūs skaičiai iš mano projektų:
– SSG su CDN: TTFB ~20-50ms, FCP ~200-400ms
– SSR su optimizacija: TTFB ~200-500ms, FCP ~400-800ms
– SSR be optimizacijos: TTFB ~500-2000ms, FCP ~1000-3000ms

Caching strategijos, kurios keičia žaidimą

Jei naudojate SSR, caching yra ne optional – tai būtinybė. Be to, jūsų serveriai neatlaikys jokio rimto traffic’o. Bet caching su Next.js gali būti tricky.

Next.js automatiškai cache’ina SSG puslapius, bet SSR puslapiai default’u nėra cache’inami. Galite pridėti cache headers rankiniu būdu:


export async function getServerSideProps({ res }) {
res.setHeader(
'Cache-Control',
'public, s-maxage=10, stale-while-revalidate=59'
);

const data = await fetchData();
return { props: { data } };
}

Šis header’is sako CDN (pvz., Vercel Edge Network): cache’ink šį response’ą 10 sekundžių, ir dar 59 sekundes gali atiduoti stale versiją, kol fone generuoji naują. Tai iš esmės SSR paverčia į kažką panašaus į ISR.

Bet būkite atsargūs su cache’inimu personalizuoto turinio. Matau bug’ų, kur vieno vartotojo duomenys patenka į kito cache’ą, nes cache key’ai neapima user ID ar session. Visada įtraukite reikalingus identifikatorius į cache key.

Deployment ir infrastruktūros niuansai

SSG deployment’as yra paprastas – tai statiniai failai. Galite juos deploy’inti į S3, Netlify, Vercel, GitHub Pages, bet kur. Infrastruktūra minimali, kaina maža, scaling’as automatinis per CDN.

SSR reikalauja serverio. Tai gali būti Node.js serveris, serverless functions, edge runtime. Kiekvienas turi savo trade-off’us:

**Traditional Node.js serveris** (pvz., EC2, DigitalOcean):
– Pilna kontrolė
– Galite optimizuoti kaip norite
– Reikia valdyti scaling’ą, monitoring’ą, updates
– Fiksuota kaina nepriklausomai nuo traffic’o

**Serverless functions** (Lambda, Vercel Functions):
– Automatinis scaling’as
– Mokate tik už naudojimą
– Cold start’ai gali būti problema
– Timeout limitai (pvz., 10s Vercel, 15min Lambda)

**Edge runtime** (Vercel Edge, Cloudflare Workers):
– Žaibiškai greitas TTFB
– Veikia arčiau vartotojų
– Apribotas runtime (ne visas Node.js API)
– Geriausias performance, bet daugiausia apribojimų

Mano rekomendacija: pradėkite su SSG + ISR kur įmanoma, naudokite serverless SSR kur reikia, ir tik tada žiūrėkite į dedicated serverius, jei turite specifinių poreikių.

Ką pasirinkti ir kaip neklyst

Po kelių metų darbo su Next.js, mano decision tree atrodo taip:

Pradėkite klausimu: ar šis puslapis gali būti statinis? Jei taip – naudokite SSG. Net jei duomenys keičiasi kas valandą ar kas dieną, ISR puikiai susitvarkys.

Jei ne, klauskite: ar duomenys priklauso nuo vartotojo? Jei taip, bet nereikia SEO (pvz., dashboard po login’o) – galbūt client-side fetching užtenka. Jei reikia SEO – SSR.

Jei duomenys nepriklauso nuo vartotojo, bet keičiasi labai dažnai – čia sudėtingiau. Galite naudoti SSG su labai trumpu revalidate (pvz., 10s), arba SSR su agresyviu cache’inimu. Testai parodys, kas veikia geriau jūsų atveju.

Praktiškai, daugelis mano projektų baigiasi su:
– 70-80% puslapių SSG (su ISR)
– 10-20% SSR (autentifikuoti puslapiai, labai dinaminiai)
– 10% pure client-side (interaktyvūs dashboard’ai)

Nebijokite eksperimentuoti ir matuoti. Next.js leidžia lengvai perjungti tarp metodų – pakeiskite getStaticProps į getServerSideProps ir pamatysite skirtumą. Naudokite Lighthouse, WebPageTest, real user monitoring. Duomenys parodo tiesą, ne teorijos.

Ir paskutinis patarimas: optimizuokite tai, kas svarbu. Jei jūsų svetainė turi 100 lankytojų per dieną, SSR vs SSG skirtumas yra nereikšmingas. Bet jei turite 100,000 – kiekviena milisekundė ir kiekvienas serverio request’as turi kainą. Skalėje smulkmenos tampa kritinėmis, o teisingas architektūrinis pasirinkimas gali sutaupyti tūkstančius infrastruktūros kaštų.

Parašykite komentarą

El. pašto adresas nebus skelbiamas. Būtini laukeliai pažymėti *