
最近想要好好的來認識實現 SSR 的 next.js 這套框架,所以就想說先回歸根本,仔細來從 CSR 開始理解到 SSR。
什麼是 CSR ?
一樣從字面上粗淺的了解一下,所謂的 CSR 指的就是「客戶端渲染 (Client-Side Rendering)」。這裡的客戶端指的是瀏覽器,再更進一步的來看,也就是在瀏覽器中產生 HTML,然後將這個 HTML 動態插入到頁面中,將畫面顯示出來。想要檢查當前的網站是不是用 CSR 這種渲染方式的話,則可以透過檢視網站原始碼來判斷,當檢視原始碼時,發現是一個和當前顯示畫面不一致、不完整的 HTML 時,就可以知道這是一個 CSR 的網頁。
如同下面這張圖呈現的樣子

就如同前面提到的 CSR 是一種客戶端渲染的渲染模式,所以我們肉眼所看到的完整頁面,都不是一開始對伺服器發送 request,再從伺服器獲得 response 後,就獲得的內容,所以當我們檢視網頁原始碼時,才會發現原始的 HTML 是一個沒有呈現完整頁面內容的 HTML。
CSR 和 SPA 的關係是?
說到 CSR,大家會想到的應該還有 SPA,那這兩者的關係究竟是什麼呢?其實 CSR 並不等於 SPA,因為 CSR 是一種渲染模式,而 SPA 則是一種應用程式的類型,也就是所謂的「單頁式應用程式 (Single-page application)」,其他像是 MPA、PWA 都是一種應用程式類型。而 CSR 和 SPA 之間的關係則是「CSR 這個渲染模式,是實現 SPA 的常見方式」,也就是說這兩者彼此的確有關係,但並不等價。
CSR 運作方式
大致上了解什麼是 CSR 後,接著來看看 CSR 的運作方式吧!
簡單來說就是「下載並執行從伺服器 response 回來的 HTML 檔案內引用的 javaScript 檔案,在瀏覽器產生完整的 HTML,以呈現完正的頁面」。從完整的流程來看的話,則是「輸入網址進入網頁 -> 對伺服器發送 reques -> 伺服器透過 response 把包含基礎內容的 HTML 檔案(只含有掛載完整內容的 <div>)和打包好的 JavaScript、CSS 等靜態資源 發送回來 -> 瀏覽器下載並執行 JavaScript -> JavaScript 在客戶端動態生成並渲染完整的 HTML 和頁面內容 -> 用戶看到完整的網頁」 。
看到這裡,你可能會有一個疑問,「response 回來的 HTML、CSS、JavaScript 是從哪產生的?」,我們可以再回到我們進行開發時的流程下去思考。當我們開發完成後,將網站部署出去前,一定會做的動作就是跑 build 的指令,通常會是 npm run build。可以觀察到,當我們執行這個 build 指令後,就會產出以下這些的檔案。

這些檔案也就是我們從伺服器 response 拿到的打包後的檔案,完整的 HTML 內容則是會在下載執行裡面的 JavaScript 檔案後,才會被產生出來。
什麼是 SSR ?
前面已經看了什麼是 CSR了,接著也來看看什麼是 SSR 吧!一樣從字面上來看的話,也就是所謂的 SSR 也就是「伺服器端渲染 (Server-Side Rendering)」,意思也就是說會直接從伺服器的 response 拿到完整的 HTML,因為 HTML 是在伺服器上產生的。 所以有別於 CSR,當嘗試用檢視網頁原始碼來查看原始的 HTML 時,就可以看到和頁面呈現內容一樣完整的 HTML。
例如以下這張圖,可以看到實際肉眼看到的內容,出現在網頁原始碼裡面。

舊時代與新時代的 SSR
看到這裡有些人可能會突然跑出一個想法,那就是「JavaScript 跑去哪了?」。我想大家應該都知道要讓一個網頁可以達到互動的效果,一定會需要 JavaScript,但是整個靜態的 HTML 都是伺服器幫我們產生,那 JavaScript 會在哪個階段出現呢?這個就不得不提到新舊時代 SSR 的差異。
就如同前面所提到的內容一樣,伺服器會幫我們產出一個完整的靜態 HTML 檔案,所以畫面呈現的部分一定是沒有問題的,但是當我們需要互動時(例如送出表單),一般的靜態的網站並沒辦法做到,那究竟是要怎麼達到這樣的效果呢?這裡就出現了新舊時代 SSR 的差異。
在舊時代的 SSR 模式中,如果想要進行送出表單之類的互動操作,會需要伺服器的幫忙,也就是說當我們送出表單時,也會向伺服器發送 request,進行相對應的處理,並且再次 response 一個新的完整 HTML,所以在做互動的動作時,整個網頁會被刷新,如果從 Dev Tools 觀察的話,就可以發現到當我們進行輸入表單的動作,或是切換頁面,都會看到有 response 回來一個完整新的 HTML,如同以下的畫面。

在新時代的 SSR 模式中,SSR 模式則不只可以處理靜態頁面,也可以優雅的處理互動操作,因為在新時代的 SSR 模式中,會有一個 hydration 的動作,也就是將 JavaScript 綁定在 DOM 元素上,這就能讓 SSR 不用在做動作時都要請伺服器處理,切換頁面也不需要重新向伺服器取得新的 HTML,也就是說其實新時代的 SSR,是一種結合 CSR 的混合渲染模式。如果觀察新時代的 SSR 網站也就可以發現雖然網頁原始碼一樣有顯示當前頁面的完整 HTML,但是當進行切換頁面或提交表單等的動作,不會再重新取得全新的 HTML。
也就會像是以下這個狀況一樣
SSR 怎麼運作
大概認識 SSR 是什麼後,接著也來看看 SSR 的運作方式是什麼。
簡單來說就是「對伺服器發送 request,取得完整的 HTML 顯示在頁面上」。從完整的流程來看的話:
- 舊時代的 SSR:「輸入網址進入網頁 -> 對伺服器發送 reques -> 伺服器產生完整的 HTML 檔案 -> 透過 response 把完整的 HTML 檔案和 CSS 等靜態資源發送回來(在 HTML 檔案中可能會包含處理特效的少量 JavaScript) -> 用戶看到完整的網頁」 。
- 新時代的 SSR:「輸入網址進入網頁 -> 對伺服器發送 request -> 伺服器產生完整的 HTML 檔案 -> 透過 response 把完整的 HTML 檔案和 CSS 等靜態資源發送回來 -> 瀏覽器下載 HTML 中用 script 引用的 JavaScript -> 將 event Handlers 綁定到 DOM 上(Hydrate) -> Hydrate 完成後,使用者就可以和頁面互動」。
從上面新舊時代的 SSR 的運作流程來看的話,就可以發現從舊時代轉換到新時代的差異,在舊時代的 SSR 模式中,就是很單純的讓伺服器產生一個靜態的檔案來呈現畫面,如果要做一些互動操作,就必須還要透過伺服器進行處理,所以也就會需要伺服器再回傳一個新的 HTML 檔案;而新時代的 SSR 模式中,除了讓伺服器產生靜態檔案來呈現畫面話,還會透過 JavaScript 才讓頁面可以在瀏覽器中進行操做,也就是 Hydration 的部分,所以只有在第一次進入畫面時,會透過伺服器回傳一個完整的 HTML 檔案,之後切換頁面就不需要再讓伺服器提供完整的 HTML 檔案了。
而我們常常會聽到的 Next.js 和 Nuxt.js 就是新時代 SSR 模式的框架。
這裡我們再稍微深入一點看看 npm run build 的時候會出現什麼檔案,以下是使用 Next.js build 之後產生的檔案,

可以發現到 SSR 模式的框架下 build 出來的檔案會比 CSR 來得複雜很多,這是因為在 SSR 模式下,不是單純使用 JavaScript 來產生完整的 HTML 內容,而是需要透過伺服器產生,產生出完整 HTML 的這個伺服器也並不是靜態伺服器(例如:Nginx),而是由框架所建構的 node server,也就是說 build 出來的檔案,除了 CSS 等的靜態檔案外,還有關於伺服器渲染的邏輯及伺服器的設定內容。
從部署看 CSR 及 SSR 的差異
接著再讓我們更進一步地去從部署流程來思考。
當我們部署 CSR 專案的時候,我們就只是很單純把 build 出來的靜態檔案放到伺服器(例:Nginx)上,當瀏覽器對伺服器發送 request 時,就是把對應的檔案 response 回去,接下來就和前面所提到過的一樣,會去下載並執行 HTML 中 script 所引用的 JavaScript 檔案,接著在瀏覽器渲染出完整的 HTML。
而部署 SSR 的專案時,就不是單純的把靜態資源放到伺服器中,而是需要透過 Next 或 Nuxt 提供的指令,將 node server 給建立起來,因為產生出完整 HTML 的伺服器是 node server,即使是部署到 Nginx 中,也不是由 Nginx 來產生完整的 HTML,還是會需要透過 Nginx 將 request 轉送給 node server,才能產生出完整的 HTML 來回應給伺服器。
CSR 和 SSR 的優點和缺點
最後再來回顧一下前面說提到的 CSR 及 SSR 的特點,也思考一下它們各自的優缺點吧!
CSR 由於伺服器回傳的 HTML 是一個不含完整內容的 HTML ,需要在瀏覽器中透過下載並執行 javaScript 檔案,才會產生完整的畫面,所以第一次進入頁面的時候,會需要等待一些時間,才會看到完整的畫面,也就是說第一次進入畫面時,會看到白色的畫面一下子,才會出現完整的畫面,但是在之後,不論是對畫面進行操作或是切換頁面,都不需要再對伺服器發送 request,所以切換頁面和進行操作時,整體呈現會比較流暢。
SSR 由於是在伺服器中產生完整的 HTML 給瀏覽器呈現,所以首次進入頁面,不會顯示白色的畫面,但是在舊時代的 SSR 模式中,因為沒有 JavaScript 的幫助,所以每次進行操作或是切換頁面時,都要重新和伺服器請求一個新的 HTML,整體的操作過程也就比較不流暢,因為會有刷新頁面的過程;但在新時代的 SSR 模式中,則是因為有 JavaScript 的幫助 (也就是前面所提到的 Hydration),使得在首次拿到完整的 HTML 之後,接著在操作及切換頁面的動作中,就可以透過 JavaScript 讓整體流程更順暢,不用一定要藉由伺服器的幫助才能進行,也就是說新時代的 SSR 結合了 SSR 和 CSR 的優點。

以上就是這次針對 CSR 及 SSR 稍微再深度一點的去了解的部分,打完收工!

