前後端分離或在一起 (CSR 與 SSR) 從把妹角度理解前後端如何和平相處

me
林彥成
2022-10-02 | 3 min.
文章目錄
  1. 1. Client Side Render (用戶端渲染)
  2. 2. Server Side Render (伺服器端渲染)
    1. 2.1. SPA 伺服器端渲染
    2. 2.2. Server Side Render 常見問題
    3. 2.3. Next.js

前後端分離或是在一起間接影響網頁渲染的方式,常見的渲染形式會有用戶端渲染 (CSR) 和伺服器端渲染 (SSR) 兩種。

這就好像男女朋友是否同居一樣,會大大影響兩人的日常生活型態,分開時兩者的自由度都較高,在一起同居時總會需要考慮彼此狀態。

前後端要分離或是在一起? 渲染的方式該怎麼決定? 小編覺得也各自有各自的優缺點,接下來就來開箱 CSR 跟 SSR 吧。

Client Side Render (用戶端渲染)

隨著瀏覽器的效能變好,帶有顯示邏輯的渲染可以轉移到瀏覽器端,於是也就出現單頁應用程式 Single Page Application (SPA) 與 Client Side Render (CSR) 的概念。

Single Page Application (SPA) 不同以往產生完整 HTML 的做法,會將網頁都打包 (bundle) 後才在用戶端執行,其中 bundle 可以想像成是 APP 的安裝與執行檔。

在 SPA 中畫面中元素不再直接產生在頁面中,初始時需要 CSR 的部分會是空白的,改由透過 API 要回來的資料和 bundle 檔在前端動態產生渲染畫面。

舉個簡單的例子來說,登入之後不需要跳轉至新的頁面並重新載入,只需要透過要回來的資料來更改網頁中的元素或元件即可。

  • 優點
    • 操作體驗較接近真實 APP,頁面間切換速度極快
    • 伺服器端壓力較小
  • 缺點
    • 第一次載入時間較慢,但可以透過 Code Splitting 優化

Server Side Render (伺服器端渲染)

HTML 由 Server 端產生,所以用戶看到的就是最終版 HTML,會有三種預渲染形式

  • 靜態生成 (Static Generation): 透過 Pre-rendering 工具預先渲染頁面產生靜態檔,HTML 在編譯階段就會生成,以小編的部落格來說就是將 Markdown 語法編譯成 HTML 的靜態檔案,在用戶請求之前頁面就已經準備並可被快取,這種渲染方式也可以跟用戶端渲染搭配著使用來加入額外資訊
  • 伺服器端渲染 (Server Side Render):在每次用戶請求時,會動態生成 HTML 效能上會比靜態生成慢,像大家常用的 WordPress 預設就是這種情況
  • 增強型伺服器端渲染 (Incremental Static Regeneration): 定期重新進行 SG 避免頁面沒有抓到較新資訊的問題

註: Pre Render 這類外掛適用於 CRA 專案但可能會跟不上 React 新功能所產生的問題

  • 優點
    • 第一次載入時間較快
    • 在 CSR 沒特殊設計的情況下 SEO 較佳
  • 缺點
    • 操作體驗較 CSR 差
    • 伺服器壓力較大

SPA 伺服器端渲染

由於網頁需等待 Bundle 完整載入後才會渲染,速度較慢即使透過 Code Splitting 去把程式碼和用到的函式庫分離開來,但仍然沒有 SSR 產生來的快速。

目前非所有搜尋引擎都能爬取 SPA 中的內容,所以理想狀況是第一頁在伺服器先進行靜態生成或伺服器端端渲染

  • 前端不需等待向後端取得資料的時間 (ex 熱門購物清單)
  • SPA 在操作上不會有整個頁面重新刷新的問題

Server Side Render 常見問題

伺服器端較簡易的實作方式是用 Node.js 語法來撰寫,理論上前端用到的套件幾乎在後端都會再用到,在實作伺服器渲染時可能會遇到的問題:

  1. 由於 SPA 是由狀態來決定元件的顯示,那伺服器端的狀態該如何決定呢?
  2. 後端渲染出來的會不會和前端渲染出來的有差異? RWD?
  3. 如果有使用狀態管理的函式庫像是 Redux 需要特別處理嗎?
  4. 前端 SPA 用的路由會影響嗎?
  5. 伺服器端 Bundle Size 變大

渲染時 react 會幫我們確認元素的 checksum 是否相同,若不同就會在 client 端重新渲染一次。

前端有用到像是 window.innerWidth 去做條件渲染時會有問題,因為 Server 沒有這些東西,必須等到元件 mount 後,意味著不可以利用這些物件來做條件渲染,不正確的狀態或是沒考慮過的操作都會讓 checksum 有錯誤,要特別謹記。

後端做 Redux 的 SSR 會搭配初始狀態加上計算後的資料產生 Store 並渲染,接著在 renderToString 時把元件轉成 html 字串並整合狀態,前端收到後再重新產生 store 傳入 APP 中。

若要減少 Server-side bundle 大小來爭取執行速度時,webpack 的 external 要設定,其他要注意的大概是 webpack target: nodelibraryTarget: commonjs2

套件們使用時需要注意的事項:

  • React Router 若有用到 router 的部分不管是前後端都要使用 match 來避免前後端不一致。
  • React-Helmet: 按照文件就是要多寫一行const helmet = Helmet.renderStatic();來避免記憶體洩漏的問題,同時若有使用樣版引擎,meta data 中的動態資料須整合
  • style-component: 是需要按照文件去注入相關資訊
  • isomorphic-fetch: 在 node 端會需要絕對路徑
  • serviceWorker: 路由衝突 CRA 專案前端可以使用 serviceWorker.unregister();

Next.js

Next.js 是由 Vercel 平台開發維護的一個 React Framework,已經把大部分 SSR 問題進行解決,如果有特殊需求官方也都有很好的範例

  • 優點
    • 減少多餘的專案設定檔
    • File System based 路由,不需額外安裝套件和設定
    • Code Splitting
    • SSR 支援
  • 缺點
    • 需要參考範例去組合出期待專案架構,對複製貼上攻城獅來說學習抄襲曲線應該可被接受
    • 思考系統設計跟架構也會需要 Next 風格,不過核心概念還是一樣的。

如果沒有太多奇怪的需求,且是有 React 開發經驗的工程師,開發初期小編覺得可以直接使用 npx create-next-app 開始進行開發。


喜歡這篇文章,請幫忙拍拍手喔 🤣


share