提到前端的渲染架構,最常聽到的就是 CSR、SSR、SSG 三個名詞。它們對網站效能的影響相當顯著,但「SSR 一定比 CSR 快」這類說法其實太過絕對。本章重新梳理這幾種主流架構,順便看看相對年輕的 ISR 解決了哪些問題。

客戶端渲染(CSR, Client-Side Rendering)#

在 React、Vue、Angular 等框架普及後,SPA(Single-Page Application)成為主流。HTML 通常只放一個容器:

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

整個流程是:

  • 伺服器回傳只有容器的 HTML。
  • 瀏覽器下載並執行 JavaScript bundle。
  • JavaScript 動態渲染元素並建立互動性。

CSR 的好處是換頁不會反白、後端壓力小,但會帶來兩個明顯問題:

  • SEO 問題:爬蟲拿到的 HTML 是空殼。
  • 效能與體驗問題:使用者必須等到 JS 載入並執行完才能看到內容。Bundle 過大時更明顯。

要緩解 SEO 問題,可以在 Web Server 偵測爬蟲並回傳預先渲染好的 HTML,或在邊緣節點(例如 Lambda@Edge、Cloudflare Workers)做 pre-rendering。

伺服器端渲染(SSR, Server-Side Rendering)#

過去的網頁普遍是傳統 SSR:伺服器處理完資料後組裝完整的 HTML 回傳。優點是頁面與資料一起送到、SEO 友善;缺點是換頁會閃爍、伺服器要做的事多。

現代我們講的 SSR 多半是 Isomorphic SSR(混合式 SSR)。流程是:

  • 伺服器先渲染出帶有資料的 HTML,回傳給瀏覽器。
  • 瀏覽器下載 JS 並做 hydration,把伺服器產出的 DOM 「補上」事件監聽器,使其具備互動性。

相比 CSR,使用者能更早看到畫面、SEO 也得到改善。但缺點是:

  • 必須維護一台 rendering server(通常為 Node.js)。
  • 程式碼要同時考量 server 與 client 兩個執行環境。
  • 在大流量下,rendering server 容易成為瓶頸。

靜態網站生成(SSG, Static Site Generation)#

SSG 在 build time 就把完整的 HTML 生出來,後續所有請求都共用同一份檔案。這也常被稱作 pre-rendering。

優點:

  • 三者中效能最好的方案。
  • 適合放到 CDN,享受邊緣快取。
  • 對 SEO 完全友善。

缺點:

  • 內容變動就要重新 build。
  • 網站越大,build time 越長。
  • 不適合需要即時或頻繁更新的內容(例如電商商品頁)。

增量靜態再生成(ISR, Incremental Static Regeneration)#

ISR 是 Next.js 9.5 提出的混合策略,介於 SSR 與 SSG 之間,特別適合「頁面很多但更新沒那麼頻繁」的情境。它的運作方式是:

  • 第一次請求某個頁面時,先回傳 fallback(例如 skeleton)。
  • 同時在伺服器端抓取資料、產生完整頁面,並丟到 CDN 快取。
  • 後續對相同頁面的請求直接命中 CDN,跟 SSG 一樣快。
  • 透過 revalidate 設定快取的有效時間,過期後在背景重新產生。
export async function getStaticProps() {
  const res = await fetch("https://...");
  const data = await res.json();

  return { props: { data }, revalidate: 60 };
}

實務參考 ISR 採用的快取策略稱作 stale-while-revalidate:先回傳舊版本,再在背景更新,使用者體驗與資源消耗都比較平衡。

Streaming SSR 與 Progressive Hydration#

傳統的 Isomorphic SSR 還是有幾個瓶頸:

  • 必須等所有 API 資料抓齊才能組 HTML。
  • 必須載入完所有 JavaScript 才能 hydration。
  • 必須 hydration 完所有元件,頁面才能互動。

Streaming SSR 讓伺服器以 chunk 的方式逐步輸出 HTML,瀏覽器可以漸進地拿到內容,FP 與 FCP 都會更快。Progressive Hydration 則讓部分元件先具備互動性,不必等整個頁面 hydrate 完。React 18 的 Suspense SSR 就是基於這個方向設計。

各架構對效能指標的影響#

評估時通常會看以下幾個指標:

  • Time to First Byte (TTFB):發出請求到拿到第一個 byte 的時間。
  • First Paint (FP):首次有任何像素被繪製的時間。
  • First Contentful Paint (FCP):首次顯示文字或圖片的時間。
  • Time to Interactive (TTI):頁面從不能互動變得可以互動的時間。

各架構的特性可以這樣對應:

  • 傳統 SSR:FP / FCP / TTI 較好,但 TTFB 較久。
  • CSR:JS bundle 過大時 FCP 與 TTI 都會被拖慢。可以用程式碼分割、Lazy Loading、preload、HTTP/2 等手段緩解。
  • SSG:靜態內容 + CDN,整體指標最佳。
  • 混合式 SSR:FCP 比 CSR 好,但 hydration 會拉長 TTI。建議只在 server 處理對 SEO 重要的資料。

該如何選擇#

實話實說:It depends。

  • 重度互動但 SEO 不那麼重要:CSR 仍是合適的選擇。
  • 需要 SEO 與動態資料:考慮 SSR。但 server side 不要扛太多計算。
  • 內容變動低、追求極致效能:SSG。
  • 頁面很多但更新頻率介於中間:ISR。

幸好像 Next.js 這類框架都已支援 page-level 的渲染策略:同一個專案中,不同頁面可以分別走 CSR、SSR、SSG 或 ISR,依需求自由搭配。

小結#

每一種渲染架構都有自己的甜蜜點,不該套用「總是 SSR 最快」之類的單一規則。理解各方案的取捨後,再針對頁面的內容特性、更新頻率與 SEO 需求做決策,網站的效能與維護成本才能取得平衡。

原文出處#