# [學習筆記] SSR、CSR、SSR with Hydration 差別 ## Server-Side Rendering ### SSR 伺服器端渲染,表示伺服器收到使用者的請求後,在伺服器生成完整的 HTML。 因為生成 HTML 的時候會在伺服器端先取得內部或外部 API 資料,所以相較於 CSR 從瀏覽器端取資料的模式取資料的模式,SSR可以省去多次的來回往返。  SSR 伺服器端渲染或稱後端渲染,並不是為了解決 CSR 問題而存在的也不是什麼新奇的概念。 當使用者請求瀏覽頁面時,後端伺服器可能就已經包含了從資料庫或其他 API 拿取資料的動作,並在後端運算渲染完最終的初始頁 HTML 後,才回應至客戶端的瀏覽器,所以瀏覽器就可以依照 HTML 繪製出初始畫面,並同時等待 JS 下載完畢後執行後續需要進行網頁互動的流程。 在網際網路與瀏覽器剛起步時,使用者的客戶端 (Client) 是與伺服器 (Server) 直接請求網頁檔案,例如 index.html,而檔案的內容即為 HTML 格式的網頁原始碼,並讓瀏覽器進行畫面的渲染繪製,我們先不考慮 JavaScript 進行互動與動態操作網頁的 HTML 元素,這裡的重點是──使用者請求某個網頁頁面後,所接收到 HTML 即是瀏覽器進行畫面渲染的最終網頁原始碼,由後端伺服器吐出最終的 HTML,雖然不是靠後端語言渲染 HTML ,但這不就也是伺服器端產生 HTML 的一種嗎! 再後來,隨著程式語言的發展,PHP、JSP 與 ASP/ASP.NET 等後端語言都是以 SSR 也就是伺服器端渲染為主,再回傳至前端。這一系列由前端發送網頁請求,後端接受到後透過後端程式碼在伺服器端執行、拿取資料庫資料等操作都在伺服端運算渲染完,再回傳至使用者的瀏覽器進行畫面的渲染繪製,也就稱之為伺服器端渲染,亦稱後端渲染。 ## SSR 的 render 時間軸  在伺服器 render 完 HTML 後傳給使用者,這時候因為已經有完整的 HTML,所以很快就來到 FCP (First Contentful Paint,使用者首次看到頁面上重要內容的時間。) 接著要下載並且執行瀏覽器端互動所需要的 JavaScript,完成後就來到了 TTI (Time to Interactive,使用者首次可以跟頁面互動的時間點)。 ## SSR 的優缺點 優點: * 需要的 JavaScript 比較少,SSR 已經在伺服器端把 render 的工作做完了,SSR 方案中瀏覽器端需要的 JavaScript 理論上會比較少,所以會比較快達到 TTI。 * 有更多的 JS 預算可以留給其他第三方 JS 使用。 * SEO 較佳,因為 SSR 產生的完整 HTML 可以很容易的被爬蟲解讀,不需要想辦法執行 JavaScript。SEO 也是大部分的人會考慮 SSR 方案的最主要原因之一。 缺點: * 較慢的 TTFB (Time to First Byte,從瀏覽頁面的動作開始到瀏覽器收到第一個 byte 所需要的時間),因為在伺服器產生完整的 HTML 很花時間。如果同時有許多人造訪 server 造成負擔很重,或是有一些非常慢的 API,都有可能讓 server 的回應速度非常慢。 * 互動性體驗差,因為 SSR 的頁面在每次互動之間都要重新讀取頁面,這在使用體驗上就不如 CSR 的頁面順暢,也是現代 web app 大多數會採用 CSR 方案的主要原因。 ## Client-side rendering ### CSR 名為客戶端渲染,表示所有的頁面渲染都透過瀏覽器端的Javascript完成。 所有邏輯、取資料、路由、template都在客戶端處理。  可以發現到因為 CSR 的特點,首頁的網頁原始碼 `<body></body>` 中,僅有 `<div id="app"></div>` 這個元素,通常這個元素就是前端框架要準備 Mount 的根元素或容器,在包含 Vue 程式碼的 JavaScript 檔案尚未下載完畢與執行前,這個容器仍舊是空的,所以也就會導致網頁是一片空白的情況。 如下圖的流程,需要一直到 Vue 程式碼下載且載入完畢後,才能開始做請求初始資料與渲染 HTML 的動作,如果今天網路環境很差,伺服器雖然已經回應回來 HTML,不過在等待 JavaScript 檔案下載的過程中白畫面依舊會持續一段時間,直到完成下載與執行渲染後觸發畫面更新。  CSR 的 HTML 只需要一個簡單的根節點: ```htmlembedded! <div id="root"></div> ``` 頁面上所有的更新都透過 Javascript 來實現。 所以請看以下例子,用Javascript每秒更新一次頁面上的時間: ```javascript! function tick() { const element = ( <div> <h1>Hello, world!</h1> <p>It's {new Date().toLocaleTimeString()}.</p> </div> ) ReactDOM.render(element, document.getElementById('root')) } setInterval(tick, 1000) ``` :::success 客戶端渲染,指的「渲染」就是在客戶端做渲染網頁的動作,瀏覽器依據變動進行畫面的更新,也因為這個特性,框架可以做的到僅渲染 Javascript 的部分,從而讓瀏覽器僅更新部分的畫面,達到更好的效能與體驗。 然而 CSR 最常為人詬病的一項缺點就是搜尋引擎最佳化 (Search Engine Optimization, SEO),一個網站上線後為了能在搜尋引擎上能有更好的排名與曝光,其中一個方式就是要為網站進行搜尋引擎最佳化的配置。 SEO 配置與優化在 CSR 上比較難去實現,因為 CSR 是在客戶端進行資料請求後渲染,再由瀏覽器做畫面更新,導致搜尋引擎的爬蟲所蒐集與索引到的網站網頁,並不包含客戶端所需要即時請求的資料,進而讓爬蟲無法解析到這些資料可能含有關鍵字等索引。雖然現今搜尋引擎的爬蟲遇到 CSR 類型的網站有部分能解決首次資料載入的問題且能收錄到資料,但仍有一些小細節仍不夠友好,綜觀來說 SPA 並不方便也不利於做 SEO 配置。 要解決 SEO 的問題,可以採用預渲染 (Pre-rendering) 的方式,讓網頁請求送達伺服器端後,返回的頁面即為已經包含資料的網頁 HTML,常見的有 Server-Side Rendering 和 Static Site Generation 等技術來解決 SEO 配置。 ::: ## CSR 的 render 時間軸  在HTML下載完之後,使用者可以看到頁面上的重要內容的時間點FCP(First Contentful Paint,第一次豐富的內容畫面)發生。 因為CSR的頁面通常只有一個根節點,所以這個時候畫面只會呈現一片空白。 (可以用 spinner、skeleton 之類的來墊檔,讓體驗好一點) 接下來會開始下載一大包 Javascript bundle,下載完之後要解析並執行 Javascript,最後才能 render 畫面。 這些都完成之後才會到 TTI(Time-To-Interactive,使用者首次可以跟頁面戶動的時間點) 在TTI之前,使用者是無法對畫面做任何動作的。 ## CSR 的優缺點 優點: 頁面更新或換頁都不需要刷新,在使用體驗上相較傳統的 SSR 應用會順暢多。 像是 Facebook 網頁版,大部分的使用者互動都不需要刷新頁面,非常順暢,使用起來就像是一個原生的 APP 一樣。 缺點: 載入速度通常較慢,尤其在低階的行動裝置上。 因為 CSR 要等待 Javascript 的下載及執行 render,所以 CSR 頁面載入的前幾秒,頁面上會沒有東西或是只有一些骨架,使用者必須要等待一段時間才能看到頁面的內容。 SEO 會較差,因為 CSR 的頁面對於爬蟲是比較不友善的。 雖然爬蟲有辦法執行 JavaScript,但爬蟲也有一些 [JavaScript render 頁面的限制](https://developers.google.com/search/docs/crawling-indexing/javascript/fix-search-javascript?hl=zh-tw)。你可以用[行動裝置相容性測試](https://search.google.com/test/mobile-friendly)這個工具來測試一個 CSR 的頁面會如何被爬蟲 render 。 你可以將[網站資源新增至 Search Console](https://search.google.com/search-console/welcome),你必須先證明自己是網站 (或網站的特定部分) 的擁有者,才能將該網站加入 Search Console 帳戶。你可以建立包含整個網域的資源 (例如 example.com),也可以建立只含單一分支的資源 (例如 example.com/clothing/)。  ## 如何優化 CSR 的效能 1. 保持 [JavaScript bundle 體積在預算以內](https://medium.com/@addyosmani/start-performance-budgeting-dabde04cf6a3)。頁面初次載入所需的 **JavaScript bundle 大小維持在 100 - 170KB** 是一個不錯的參考值。 3. 使用 preload 提前 JavaScript bundle 的下載。 > preload 告訴瀏覽器:「這份資源對目前的頁面是必要的,請用最快的速度下載此資源。」 使用方法如下: ```javascript! <link rel="preload" as="script" href="super-important.js"> <link rel="preload" as="style" href="critical.css"> ``` as 是用來指定資源的類別的。這個屬性需要指定,不然可能會重複下載同一份資源。 preload 對瀏覽器有「強制作用」而非「建議」,所以你必須很確定它是真正重要的資源。 雖然瀏覽器可以先掃一遍 html 提早發現資源,但是有些「隱藏」在 CSS/JS 內的資源就沒辦法了。這時候用 preload 就非常有幫助。例如: CSS 中的字體檔。 script 中動態載入其他 script/CSS 等。 3. 使用 code splitting 拆分 JavaScript bundle。 保持 JavaScript bundle 在預算內是非常困難的事情,因為 JavaScript bundle 的大小,通常會隨著應用的開發越長越大。其中一個可行的做法是採用 code splitting 的方法,也就是只在真正需要某個 JavaScript 的時候才去下載拆分出來的片段。[Webpack 等打包工具支援 code splitting](https://webpack.js.org/guides/code-splitting/)。 如果想看一些 code splitting 的真實案例,可以參考來原作者[寫的這篇網頁載入效能優化](https://www.shubo.io/optimize-loading-speed/) (Web Performance Optimization) (加速 30% 真實案例分享) ## SSR with Hydration SSR with Hydration,也被稱為 Universal Rendering,是一種透過 hydration 結合了 CSR 與 SSR 的方案,其 render 的流程是: ## SSR with hydation 的 render 時間軸  1. 在伺服器端產生靜態 HTML 並傳送給使用者。 1. 接著在客戶端透過 hydration 的過程讓網頁具有互動性。 1. 由客戶端的 JavaScript 接手後續的 render 工作。 #### SSR with Hydration 兼具了 SSR 的快速 FCP、SEO 友善,同時又有 CSR 的高互動性。  1. 首先使用者的請求會透過伺服器處理,產生完整的 HTML 以後,用來 render 的資料和 JavaScript 一起被嵌入 HTML,傳送給客戶端。因為已經有完整的 HTML,瀏覽器收到以後可以很快的畫出頁面,達到如同傳統 SSR 的快速 FCP,也就是使用者首次看到頁面上重要內容的時間。 1. 接著瀏覽器會下載 JavaScript bundle,下載完並解析 JavaScript 後,客戶端會執行 hydration 的步驟,完成後達到 TTI,也就是使用者開始可以跟頁面互動。 1. 之後的 render 工作則會由客戶端的 JavaScript 接手,達到 CSR 的高互動性。 ### Hydration 是什麼? Hydration 指的是在客戶端透過 JavaScript 讓伺服器端產生的 HTML 加上 event handler (事件處理器),使其獲得互動能力的一種過程。 在完成 hydration 之前,使用者沒辦法和伺服器端產生出來的頁面互動。必須要等待 hydration 完成,使用者才有辦法跟頁面互動。 ## 結論: CSR => **Client 的拿到的是 html 的框架,但內容都是空的** SSR => **Server 的話就是客戶端拿到的已經是寫好的 html** SSR with hydation => **起初拿的 html 就是完整的,之後的 CSR 會交給 hydation** 本質上都要架 Server,只是看渲染是由前後端哪一端做。 * 純SSR是透過同步的HTTP協定拿到 HTML,如果要更新資料,要靠刷新頁面,不能動態更新,就是古早那種<form action="POST">,然後按下去submit會跳頁,這種就是很純的SSR。 * 現在 Nuxt 或是 Next 這類 SSR+CSR 的框架本質上還是前端做 Server,他們還是依靠 API 去取得資料,API大部分都是透過AJAX去取得,真正的 SSR 是不需要靠 API 的。 他們只有一開始是 SSR,當客戶取得 Javascript 之後,把#app的div換掉,就會變成 CSR ,所以 Nuxt 只有一開始會拿到靜態,後面會自己加載 Javascript,就會轉成 CSR,這種又稱為 hydration。 一開始就會有 `<div id="app">`,vite ssr 是在中間加入一個標示字符,SSR 會去把裡面整段標示字符換成 html,之後變成 CSR 的時候,再把 VDOM 掛上去  ※ 資料來源:https://www.shubo.io/rendering-patterns/#csr-client-side-rendering ※ 資料來源:https://ithelp.ithome.com.tw/articles/10291291
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up