Server-side rendering prieš client-side rendering: techninis palyginimas

Kodėl vis dar ginčijamės dėl renderinimo?

Kiekvieną kartą, kai prasideda naujas projektas, kyla ta pati diskusija. Viena komandos dalis tvirtai laikosi SSR, kita – prisiekia CSR pranašumais. Ir žinot ką? Abi pusės dažniausiai būna teisingos, tik skirtinguose kontekstuose.

Renderinimo strategijos pasirinkimas nėra vien techninis sprendimas – tai kompromisas tarp našumo, SEO, kūrėjų patirties ir projekto tikslų. Per pastaruosius kelerius metus dirbu su įvairiausiais projektais, nuo e-komercijos platformų iki SaaS aplikacijų, ir galiu pasakyti, kad universalaus atsakymo nėra. Bet yra aiškūs kriterijai, kurie padeda priimti teisingą sprendimą.

Šiame straipsnyje pažvelgsime į SSR ir CSR ne tik teoriškai, bet ir praktiškai – su konkrečiais pavyzdžiais, metrikomis ir realiais scenarijais, su kuriais susidursit savo projektuose.

Kaip iš tikrųjų veikia server-side rendering

SSR principas paprastas: serveris gauna užklausą, sugeneruoja pilną HTML puslapį su visais duomenimis ir atsiunčia jį naršyklei. Naršyklė gauna jau paruoštą turinį, kurį gali iškart rodyti vartotojui. Vėliau, kai JavaScript failai atsisiunčia ir įsijungia, puslapis tampa interaktyvus – šis procesas vadinamas „hydration”.

Praktikoje tai atrodo taip: kai vartotojas užeina į produkto puslapį, serveris iškviečia duomenų bazę, gauna produkto informaciją, įterpia ją į React/Vue/bet kokį kitą komponentą, sugeneruoja HTML ir atsiunčia. Naršyklė gauna kažką panašaus:


<div class="product">
<h1>iPhone 15 Pro</h1>
<p class="price">1299 EUR</p>
<button id="add-to-cart">Į krepšelį</button>
</div>

Viskas jau čia, tekstas matomas, SEO robotai skaito be problemų. Bet mygtukas dar neveikia – reikia palaukti, kol atsisiųs ir įsijungs JavaScript.

Čia ir slypi pirmasis SSR iššūkis – Time to Interactive (TTI) gali būti ilgesnis nei First Contentful Paint (FCP). Vartotojas mato turinį, bet negali su juo sąveikauti. Tai vadinama „uncanny valley” efektu – kai puslapis atrodo paruoštas, bet dar nereaguoja į veiksmus. Itin erzinanti patirtis, ypač lėtesniuose įrenginiuose.

Next.js ir kiti framework’ai

Šiuolaikiniai SSR framework’ai kaip Next.js, Nuxt.js ar SvelteKit labai supaprastino SSR implementaciją. Next.js, pavyzdžiui, leidžia pasirinkti renderinimo strategiją kiekvienam puslapiui atskirai:


// Static Generation
export async function getStaticProps() {
const data = await fetchData();
return { props: { data } };
}

// Server-Side Rendering
export async function getServerSideProps(context) {
const data = await fetchData(context.params.id);
return { props: { data } };
}

Tai suteikia lankstumą – galite naudoti SSR tik ten, kur tai tikrai reikalinga. Blog’o straipsniai? Static generation. Produkto puslapis su kintančiomis kainomis? SSR. Vartotojo dashboard’as? CSR.

Client-side rendering realybė

CSR veikia visiškai kitaip. Serveris atsiunčia minimalų HTML failą, kuris dažniausiai atrodo maždaug taip:


<html>
<body>
<div id="root"></div>
<script src="bundle.js"></script>
</body>
</html>

Visas turinys generuojamas naršyklėje, kai įsijungia JavaScript. Aplikacija atsisiunčia duomenis per API, sugeneruoja DOM elementus ir atvaizduoja juos ekrane.

Skamba neefektyviai? Ne visada. Moderniose single-page aplikacijose (SPA), kur vartotojas praleidžia daug laiko ir atlieka daug veiksmų, CSR gali būti greitesnis ir sklandesnis. Kodėl? Nes po pradinio įkėlimo, viskas vyksta vietoje – nereikia laukti serverio atsakymo kiekvienam veiksmui.

Pavyzdžiui, Gmail ar Facebook – tai CSR aplikacijos. Pirmasis įkėlimas gali užtrukti, bet paskui viskas veikia žaibiškai. Paspaudėte ant laiško – jis atsidaro akimirksniu. Perjungėte į kitą folderį – turinys pasikeičia be jokio puslapio perkrovimo.

Bundle size problema

Didžiausia CSR problema – JavaScript bundle dydis. Kuo daugiau funkcionalumo, tuo didesnis failas. Ir kol tas failas neatsisiųs ir neįsijungs, vartotojas mato tuščią ekraną arba loading’ą.

Realūs skaičiai iš projektų, su kuriais dirbau:
– Paprasta landing’o aplikacija: ~150KB (gzipped)
– Vidutinio dydžio e-komercija: ~400-600KB
– Sudėtinga enterprise SaaS: 1-2MB+

Tie megabaitai turi būti ne tik atsisiųsti, bet ir parse’inti bei įvykdyti. Ant iPhone 7 ar panašaus įrenginio, 1MB JavaScript gali užtrukti 3-4 sekundes vien parse’inimui. Pridėkite tinklo laiką ir turite 5-6 sekundžių tuščią ekraną.

Sprendimas? Code splitting, lazy loading, tree shaking. React.lazy(), dynamic imports, route-based splitting – visa tai padeda, bet reikalauja disciplinos ir nuolatinio bundle size monitoringo.

SEO ir crawling’o niuansai

Čia prasideda įdomiausia dalis. Teoriškai, Google roboto Googlebot jau seniai sugeba vykdyti JavaScript ir indeksuoti CSR aplikacijas. Praktiškai – ne viskas taip rožių spalvų.

Pirma, ne visi search engine’ai vienodai geri. Google – taip, neblogai. Bing – taip pat. Bet socialinių tinklų crawler’iai (Facebook, Twitter, LinkedIn) dažnai nesugeba vykdyti JavaScript. Tai reiškia, kad jūsų gražiai suformatuoti Open Graph meta tagai, kurie generuojami JS’e, tiesiog nebus matomi.

Antra, net ir Google turi apribojimų. Jei jūsų aplikacija per ilgai kraunasi arba JavaScript klaidos trukdo renderinimui, puslapis gali būti neindeksuojamas arba indeksuojamas su nepilnu turiniu.

Trečia, rendering budget. Google skiria ribotą laiką ir resursus kiekvieno puslapio renderinimui. Jei jūsų aplikacija per sudėtinga, roboto gali neužtekti kantrybės.

Praktiniai SEO patarimai

Jei vis tiek pasirenkate CSR ir jums svarbu SEO:

1. Naudokite prerendering – servisai kaip Prerender.io ar Rendertron sugeneruoja statiškas puslapių versijas crawler’iams
2. Implementuokite dynamic rendering – aptikite botus ir jiems atiduokite SSR versiją, o žmonėms – CSR
3. Skeleton screens – bent jau rodykite struktūrą, kol kraunasi turinys
4. Meta tagus dėkite į HTML – bent pagrindinius (title, description, og:tags) turėkite serveryje

Asmeniškai mačiau projektų, kur CSR aplikacijos puikiai reitinguojasi Google. Bet tai reikalauja papildomo darbo ir nuolatinio monitoringo. Su SSR tiesiog veikia iš karto.

Našumo metrikos ir realūs skaičiai

Teorija be skaičių – tuščios kalbos. Padariau kelis testus su identiška aplikacija, implementuota SSR (Next.js) ir CSR (Create React App) būdais. Testuota Lighthouse, WebPageTest, realūs įrenginiai.

SSR rezultatai (Next.js):
– First Contentful Paint: 0.8s
– Largest Contentful Paint: 1.2s
– Time to Interactive: 2.1s
– Total Blocking Time: 180ms
– Cumulative Layout Shift: 0.02

CSR rezultatai (CRA):
– First Contentful Paint: 1.8s
– Largest Contentful Paint: 2.4s
– Time to Interactive: 3.2s
– Total Blocking Time: 420ms
– Cumulative Layout Shift: 0.08

Skirtumai akivaizdūs. Bet štai įdomesnis dalykas – kai vartotojas naršo po aplikaciją, CSR versija jaučiasi greitesnė. Navigacija tarp puslapių CSR: ~100-200ms. SSR: ~400-600ms (nes reikia serverio round-trip).

Core Web Vitals kontekste

Google Core Web Vitals tapo rimtu reikalu. LCP, FID, CLS – šios metrikos dabar tiesiogiai veikia SEO. Ir čia SSR turi aiškų pranašumą, bent jau pirmajame puslapio įkėlime.

Bet yra niuansas – jei jūsų SSR aplikacija daro daug sunkių operacijų serveryje, TTFB (Time to First Byte) gali būti prastas. Mačiau projektų, kur SSR puslapis generavosi 2-3 sekundes serveryje, nes darė 5-6 duomenų bazės užklausas. Tokiu atveju CSR su gerai optimizuotu API būtų greitesnis.

Sprendimas? Caching’as, duomenų bazės optimizavimas, CDN, edge computing. Arba hibridinis požiūris.

Hibridiniai sprendimai ir ateities perspektyvos

Realybė tokia, kad daugelis šiuolaikinių aplikacijų naudoja hibridinį požiūrį. Next.js tai vadina „Incremental Static Regeneration” (ISR), Gatsby – „Deferred Static Generation”. Idėja paprasta: generuojate statišką turinį build metu, bet galite jį atnaujinti pagal poreikį.

Pavyzdys: e-komercijos produktų katalogas. Turite 10,000 produktų. Visi sugeneruoti statiškai build metu? Neefektyvu. Visi SSR? Serveris pavargs. Sprendimas:


export async function getStaticProps({ params }) {
return {
props: { product: await getProduct(params.id) },
revalidate: 60 // Atnaujinti kas 60 sekundžių
};
}

export async function getStaticPaths() {
return {
paths: [], // Negeneruoti nieko build metu
fallback: 'blocking' // Generuoti pagal poreikį
};
}

Pirmasis vartotojas, užėjęs ant produkto, gauna SSR versiją. Ji sugeneruojama ir cache’inama. Kiti vartotojai gauna cache’intą versiją. Po 60 sekundžių, jei kas nors vėl užeina, cache atnaujinamas. Geriausias iš abiejų pasaulių.

Islands Architecture

Astro ir kiti framework’ai propaguoja „islands architecture” koncepciją. Idėja: dauguma puslapio – statiškas HTML, o interaktyvūs komponentai („salos”) – CSR.

Pavyzdžiui, blog’o straipsnis – statiškas HTML. Bet komentarų sekcija – interaktyvi React komponenta. Produkto aprašymas – statiškas. Bet „Į krepšelį” funkcionalumas – interaktyvus.

Tai leidžia drastiškai sumažinti JavaScript kiekį. Vietoj 500KB bundle, gaunate 50KB tik tam, kas tikrai turi būti interaktyvu. Kitas turinys – paprastas, greitas HTML.

Kada rinktis ką: praktiniai scenarijai

Gana teorijos, pereikime prie konkrečių rekomendacijų.

Rinkitės SSR, kai:
– SEO kritiškai svarbus (marketing’o svetainės, blog’ai, e-komercija)
– Turinys dažnai keičiasi ir turi būti aktualus
– Vartotojai dažniausiai užeina tik į vieną puslapį ir išeina
– Turite lėtų įrenginių auditoriją
– Socialinio sharing’o meta tagai svarbūs

Rinkitės CSR, kai:
– Kuriate aplikaciją, ne svetainę (dashboard’ai, admin panelės)
– Vartotojai praleidžia daug laiko ir atlieka daug veiksmų
– Turinys už authentication sienos (SEO nereikalingas)
– Reikia realtime funkcionalumo
– Turite stiprią backend API infrastruktūrą

Rinkitės hibridinį, kai:
– Turite ir viešą turinį, ir privatų funkcionalumą
– Norite geriausio našumo visose srityse
– Turite resursų sudėtingesnei architektūrai palaikyti
– Projektas ilgalaikis ir vystysis

Realūs projektų pavyzdžiai

Iš savo praktikos galiu pasidalinti keliais case’ais:

E-komercijos platforma (SSR + ISR): Produktų puslapiai – ISR su 5 minučių revalidation. Checkout – CSR su optimistic updates. Rezultatas: puikus SEO, greita vartotojo patirtis, sumažėjusi serverio apkrova 60%.

SaaS dashboard’as (CSR): Visa aplikacija už login’o, daug realtime duomenų, sudėtinga logika. CSR su React Query cache’inimu. Pirmasis įkėlimas 2.5s, bet paskui viskas veikia akimirksniu.

Naujienų portalas (SSR): Straipsniai – static generation su on-demand revalidation. Breaking news – SSR. Komentarai – CSR komponentas. Idealus balansas tarp SEO ir interaktyvumo.

Ką reikia žinoti prieš priimant sprendimą

Renderinimo strategijos pasirinkimas – ne vienkartinis sprendimas. Tai įsipareigojimas tam tikrai architektūrai, infrastruktūrai ir darbo procesams.

SSR reikalauja serverio. Tai reiškia hosting’o kaštus, scaling’o iššūkius, deployment’o sudėtingumą. Vercel, Netlify ir panašios platformos tai supaprastina, bet vis tiek brangu, kai turite didelį traffic’ą. Mačiau projektų, kur SSR hosting’as kainavo 10x daugiau nei paprastas static hosting’as.

CSR reikalauja geresnio frontend’o. Bundle optimization, code splitting, state management – visa tai tampa kritiškai svarbu. Jei komanda neturi patirties, galite gauti lėtą, sunkią aplikaciją.

Hibridiniai sprendimai reikalauja abiejų kompetencijų. Bet suteikia geriausią rezultatą, jei turite resursų.

Dar vienas aspektas – development experience. SSR gali komplikuoti development’ą. Dalys kodo vyksta serveryje, dalys – kliente. Reikia galvoti apie window objekto nebuvimą serveryje, cookies, headers. CSR paprastesnis – viskas vyksta naršyklėje.

Bet SSR framework’ai kaip Next.js labai supaprastino šį procesą. File-based routing, automatic code splitting, built-in API routes – visa tai daro SSR development’ą beveik tokį pat paprastą kaip CSR.

Paskutinis, bet ne mažiau svarbus dalykas – komandos įgūdžiai. Jei jūsų komanda puikiai išmano React ir state management, bet niekada nedirbo su SSR, galbūt geriau pradėti nuo CSR ir palaipsniui pereiti prie hibridinio. Jei turite fullstack kūrėjus, kurie jaučiasi komfortiškai ir frontend’e, ir backend’e – SSR bus natūralus pasirinkimas.

Technologijų pasaulis juda link hibridinių sprendimų. React Server Components, Qwik resumability, Svelte runes – visos šios technologijos bando sujungti SSR greitį su CSR interaktyvumu. Ateitis tikrai ne „arba-arba”, o „ir-ir”.

Tad neužsifiksukite ties vienu sprendimu. Pradėkite nuo to, kas atitinka jūsų dabartines problemas, bet palikite duris atidarytas evoliucijai. Geras architecture leidžia keisti renderinimo strategijas pagal poreikį, neperrašant visos aplikacijos. Ir būtent to turėtumėte siekti – lankstumo, ne dogmatiško laikymosi vienos technologijos.

Parašykite komentarą

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