關注「前端向後」微信公衆號,你將收穫一系列「用心原創」的高質量技術文章,主題包括但不限於前端、Node.js以及服務端技術html
一.SSR 簡介
SSR(Server-Side Rendering)並非什麼新奇的概念,先後端分層以前很長的一段時間裏都是以服務端渲染爲主(JSP、PHP),在服務端生成完整的 HTML 頁面前端
之因此要在服務端完成組件渲染工做,是由於有性能與可訪問性兩大優點瀏覽器
二.2 大優點
性能
與 CSR(Client-side rendering)模式相比,SSR 的性能優點體如今 2 方面:緩存
網絡鏈路服務器
省去了客戶端二次請求數據的網絡傳輸開銷微信
服務端的網絡環境要優於客戶端,內部服務器之間通訊路徑也更短cookie
內容呈現網絡
首屏加載時間(FCP)更快併發
瀏覽器內容解析優化機制可以發揮做用
網絡鏈路上,由服務端發出接口請求,將返回數據隨 HTML 響應內容一次性傳遞到客戶端,比 CSR 二次請求更快。而且服務端網絡傳輸速度更快(能夠有更大帶寬)、通訊路徑更短(能夠同機房部署)、通訊效率也更高(能夠走 RPC)
內容呈現方面,CSR 的 HTML 大可能是個空殼兒:
<!DOCTYPE html>
<html>
<head>
<title>My Awesome Web App</title>
<meta charset="utf-8">
</head>
<body>
<div id="app"></div>
<script src="bundle.js"></script>
</body>
</html>
客戶端拿到這種 HTML 只能當即渲染出一頁空白,二次請求的數據回來以後才能呈現出有意義的內容,而 SSR 返回的 HTML 是有內容(數據)的,客戶端可以馬上渲染出有意義的首屏內容(First Contentful Paint)。同時,靜態的 HTML 文檔讓流式文檔解析(streaming document parsing)等瀏覽器優化機制也能發揮其做用
關鍵區別是 SSR 不依賴客戶端環境,包括網絡環境和設備性能,即便用戶的網絡狀況很糟(弱網)、設備性能不好(廉價、老舊設備),服務端渲染一樣可以保障與最優用戶環境(Wi-Fi 網絡、高端設備)下相近的內容加載體驗
可訪問性
可訪問性(accessibility)從兩方面理解:
對人:古老、特殊的用戶設備,好比禁用了 JavaScript
對機器人:爬蟲程序等,典型的,搜索引擎爬蟲
前者通常沒必要太過在乎,後者要關注兩大「客戶」:
搜索引擎:SEO
社交媒體:抓取頁面內容展現縮略信息(好比 Twitter 卡片等)
對 PC 站點而言,保證搜索引擎可以正確索引、準確理解頁面內容,有重要的商業價值(搜索結果靠前,曝光量更大)。移動端雖沒必要考慮搜索引擎爬取,但也有相似的社交分享需求,社交媒體會抓取目標頁面中的圖片等做爲縮略信息
P.S.誠然,有些搜索引擎可以正確爬取重 CSR 的 SPA,但不是所有,而且一大批社交媒體大都只從響應 HTML 中提取部份內容做爲縮略信息,動態渲染 HTML(部分)內容的需求真切存在
雖具備這些優點,但 SSR 卻遠不如 CSR 應用普遍,是由於 SSR 面臨着 6 大難題
三.6 個難題
難題 1:如何利用存量 CSR 代碼實現同構
爲了降級、複用、下降遷移成本等目的,一般會採用一套 JavaScript 代碼跨客戶端、服務端運行的同構方式來實現 SSR,然而,要讓現有的 CSR 代碼在服務端跑起來,先要解決諸多問題,例如:
客戶端依賴:分爲 API 依賴和數據依賴兩種,好比
window/document
之類的 JS API、設備相關數據信息(屏幕寬高、字體大小等)生命週期差別:例如 React 中,
componentDidMount
在服務端不執行異步操做不執行:服務端組件渲染過程是同步的,
setTimeout
、Promise
之類的都等不了依賴庫的適配:React、Redux、Dva 等等,甚至還有第三方庫等不肯定可否跑在 universal 環境,是否須要跨環境共享狀態,以狀態管理層爲例,SSR 要求其 store 必須是可序列化的
兩邊共享狀態:每一份須要共享的狀態都要考慮(服務端)如何傳遞、(客戶端)如何接收
難題 2:服務的穩定性和性能要求
與客戶端程序相比,服務端程序對穩定性和性能的要求嚴苛得多,例如:
穩定性:異常崩潰、死循環
性能:內存/CPU 資源佔用、響應速度(網絡傳輸距離等都要考慮在內)
所以面臨後端專業性問題,Demo 級的 SSR 可能並不難,但高可用的 SSR 服務卻絕非易事,如何應對大流量/高併發,如何識別故障,如何降級/快速恢復,哪些環節須要加緩存,緩存如何更新……
難題 3:配套設施的建設
SSR 最核心的部分是渲染服務,但除此以外還要考慮:
本地開發套件(校驗 + 構建 + 預覽/HMR + 調試)
發佈流程(版本管理)
一整套的工程設施,在 SSR 模式下都須要從新考慮
難題 4:錢的問題
引入 SSR 渲染服務,實際上實在網絡結構上加了一層節點,而大流量所過之處,每一層都是錢:
Most importantly, SSR React apps cost a lot more in terms of resources since you need to keep a Node server up and running.
將組件渲染邏輯從客戶端改到服務器執行,計算資源的成本必須考慮在內
難題 5:hydration 的性能損耗
客戶端接到 SSR 響應以後,爲了支持(基於 JavaScript 的)交互功能,仍然須要建立出組件樹,與 SSR 渲染的 HTML 關聯起來,並綁定相關的 DOM 事件,讓頁面變得可交互,這個過程稱爲 hydration
hydration 所需加載、執行的 JavaScript 代碼不見得比 CSR 模式少多少,這部分工做在客戶端執行,受限於用戶設備的性能,在較差的設備下可能會形成可感知的不可交互時間:
CSR:可交互可是沒有數據(還在異步請求數據,可能會持續很長)
SSR:有數據可是不可交互(拉到 JS 後開始 hydrate 的過程,能看到內容可是不可交互,通常不會持續很長)
富交互的場景下,後者不必定比前者用戶體驗更好
難題 6:數據請求
服務端同步渲染要求先發請求,拿到數據後纔開始渲染組件,那麼面臨 3 個問題:
數據依賴要從業務組件中剝離出來
缺失客戶端公參(包括 cookie 等客戶端會默認帶上的 header 信息)
兩邊數據協議不一樣:服務端可能有更高效的通訊方式,好比 RPC
目前主流的 CSR 模式下,數據依賴與業務組件存在緊耦合,要由服務端發起的數據請求全都摻雜在組件生命週期函數中,剝離數據依賴意味着須要同時改造 CSR 代碼。公參、數據協議等差別對代碼複用、可維護性也提出了一些新的挑戰
四.應用場景
不管首屏加載性能仍是可訪問性,都是對內容密集型頁面纔有意義,而對於交互密集型的頁面,SSR 所能提早渲染的內容很少,對用戶意義不大,SEO 的必要性也值得商榷。所以,SSR 適用於偏靜態的內容展現場景,典型的,商品詳情、攻略、文章等圖文混排的場景
另外一方面,不必定非要 100% SSR,渲染特定頁面,甚至只渲染個頁面框架也是不錯的應用:
「Application Shell」 is an excellent concept. But sometimes, we might need to render a part of the page in the server. It could be the header with user info. In such cases, you need server-side rendering.
參考資料
A PAIN IN THE REACT: CHALLENGES BEHIND SSR
Why it’s tricky to measure Server-side Rendering performance:angular SSR 與 CSR 對照實測,數據可參考
Hey Next.js, Is Server Side Rendering Dead?
本文分享自微信公衆號 - 前端向後(backward-fe)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。