關注「前端向後」微信公衆號,你將收穫一系列「用 💖 原創」的高質量技術文章,主題包括但不限於前端、Node.js以及服務端技術php
寫在前面
SSR(Server-Side Rendering)並非什麼新奇的概念,先後端分層以前很長的一段時間裏都是以服務端渲染爲主(JSP、PHP),在服務端生成完整的 HTML 頁面css
(摘自《前端渲染模式的探索》)html
也就是說,歷經 SSR 到 CSR 的大變革以後,現在又從 CSR 出發去探索 SSR 的可能性……彷佛兜兜轉轉又回到了起點,在這之間發生了什麼?現在的 SSR 與當年的 JSP、PHP 又有什麼區別?前端
一.SSR 大行其道
回到論壇、博客、聊天室仍舊火熱的年代,行業最佳實踐是基於 JSP、PHP、ASP/ASP.NET 的動態網站nginx
以 PHP 爲例:後端
<?php if ( count( $_POST ) ): ?>
<?php include WTG_INCPATH . '/wechat_item_template.php' ?>
<div style="...">
<div id="wechat-post" class="wechat-post" style="...">
<div class="item" id="item-list">
<?php
$order = 1;
foreach ( $_POST['posts'] as $wechat_item_id ) {
echo generate_item_list( $wechat_item_id, $order );
$order++;
}
?>
</div>
<?php
$order = 1;
foreach ( $_POST['posts'] as $wechat_item_id ) {
echo generate_item_html( $wechat_item_id, $order );
$order++;
}
?>
<fieldset style="...">
<section style="...">
<p style="...">若是心中仍有疑問,請查看原文並留下評論噢。(<span style="font-size:0.8em; font-weight:600">特別要緊的問題,能夠直接微信聯繫 ayqywx</span> )</p>
</section>
</fieldset>
</div>
<script>
function refineStyle () {
var post = document.getElementById('wechat-post');
// ul ol li
var uls = post.getElementsByTagName('ul');
for (var i = uls.length - 1; i >= 0; i--) {
uls[i].style.cssText = 'padding: 0; margin-left: 1.8em; margin-bottom: 1em; margin-top: -1em; list-style-type: disc;';
uls[i].removeAttribute('class');
};
}
document.addEventListener('DOMContentLoaded', function() {
refineStyle();
});
</script>
</div>
<?php endif ?>
(摘自ayqy/wechat_subscribers,一款用來自動生成微信公衆平臺圖文消息的 WordPress 插件)瀏覽器
這一時期網頁內容徹底由服務端渲染,客戶端(瀏覽器)接收到的是融合了服務數據的 HTML,以及少許內聯的(表單)交互邏輯和樣式規則,支撐着早期大量動態網站的正是這種純 SSR 模式緩存
但隨着技術實踐的深刻,這種模式逐漸暴露出了一些問題:服務器
性能差:每個請求過來都要從新執行一遍數據邏輯和視圖邏輯,動態生成 HTML,即使其中很大一部份內容是相同的微信
機器成本高:Tomcat/Apache 等應用服務器的併發處理能力遠不及nginx之類的 Web 服務器,所以須要部署更多的機器
開發/維護難:先後端代碼摻雜在一塊兒,人員協做是個問題,而且修改維護要十分謹慎(標籤結構容易被破壞)
面對這些問題,兩個思路逐漸變得清晰起來,動靜分離與先後端分層,前者解決性能和機器成本的問題,後者解決開發/維護的問題
二.動靜分離
爲了充分利用 Web 服務器的靜態資源處理優點,同時減輕應用服務器的負擔,將資源分爲兩類:
靜態資源:圖片、CSS、JS 等公用的,與具體用戶無關的資源
動態資源:應用邏輯、數據操做等與具體用戶密切相關的資源
兩種資源分開部署,把靜態資源部署至 Web 服務器或 CDN,應用服務器只部署動態資源。如此這般,靜態資源響應更快了(瀏覽器緩存、CDN 加速),應用服務器壓力更小了,皆大歡喜
然而,視圖邏輯卻被咱們漏掉了,HTML 算做靜態資源仍是動態資源?
先後端分層就是爲了回答這個問題
三.先後端分層
視圖邏輯的特殊之處在於:
與數據密切相關
服務端與客戶端都可承載視圖邏輯
也就是說,HTML 視圖結構的建立和維護工做,能夠由服務端完成,也能夠在客戶端完成,都依賴服務數據。但與服務端相比,客戶端環境有一些優點:
無需刷新(從新請求頁面)便可更新視圖
免費的計算資源
所以,視圖邏輯劃分到了客戶端(即 CSR),以數據接口爲界,分紅先後端兩層:
後端:提供數據及數據操做支持
前端:負責數據的呈現和交互功能
自此,先後端各司其職,前端致力於用戶體驗的提高,後端專一業務領域,並行迭代,(不涉及接口變化時)互不影響
四.CSR 如日中天
先後端分層以後,進入了 CSR 的黃金時代,探索出了功能插件、UI 庫、框架、組件等多種代碼複用方案,最終造成了繁榮的組件生態
組件化的開發方式之下,純 CSR 模式日益盛行:
<!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>
這種模式下,幾乎全部的頁面內容都由客戶端動態渲染而來,包括建立視圖、請求數據、融合數據與模版、交互功能在內的全部工做,都交由一套數據驅動的組件渲染機制來全權管理,而沒必要再關注組件之下的 DOM 結構維護等工做,有效提升了前端的生產效率。但一些問題也隨之而來:
在組件樹首次渲染完以前,頁面上沒法展現任何內容,包括 loading
數據請求必須等到所屬組件開始渲染才能發出去
這些問題的根源在於目前的組件渲染流程是同步阻塞的,對首屏性能提出了挑戰:
低端設備上 JS 執行效率低,白屏時間長
弱網環境下數據返回慢,loading 時間長
CSR 雖然利用了用戶設備的計算資源,但同時也受其性能、網絡環境等不可控因素的制約。因而,你們又從新將目光彙集到了 SSR
五.SSR 東山再起
SSR 模式下,首屏內容在服務端生成,客戶端收到響應 HTML 後可以直接呈現內容,而無需等到組件樹渲染完畢
雖然核心思想都是在服務端完成頁面渲染工做,但現在的 SSR 與先前大不相同,體如今:
出發點:爲了更快、更穩定地呈現出首屏內容
成熟度:創建在前端成熟的組件體系、模塊生態之上,基於 Node.js 的同構方案成爲最佳實踐
獨立性:仍然保持着先後端分層,不與業務領域的應用服務強耦合
也就是說,現在的 SSR 是爲了解決前端層的問題,結合 CSR 優化內容加載體驗,是在 CSR 多年積澱之上的擴展,與現有的前端技術生態保持着良好的相容性。而當年的 SSR 更多地是爲了實現功能,解決溫飽問題
再看當年 SSR 面臨的幾個問題:
性能差:每個請求過來都要從新執行一遍數據邏輯和視圖邏輯,動態生成 HTML,即使其中很大一部份內容是相同的
機器成本高:Tomcat/Apache 等應用服務器的併發處理能力遠不及nginx之類的 Web 服務器,所以須要部署更多的機器
開發/維護難:先後端代碼摻雜在一塊兒,人員協做是個問題,而且修改維護要十分謹慎(標籤結構容易被破壞)
引入 SSR 以後這些問題捲土重來,但這些年的技術發展爲解決這些問題提供了新的思路:
實時渲染的性能問題:動靜分離的思路仍然適用,例如Static Generation
服務器資源成本問題:雲計算的發展有望大幅下降機器成本,例如Node FaaS
SSR 部分與 CSR 部分的開發/維護問題:同構爲解決開發/維護難題提供了一種新思路(以前的思路是先後端分層,但這一次分不開了),維護同一份代碼,跑在不一樣的運行環境輸出不一樣形式的目標產物
其中,Static Generation(也叫 SSG,Static Site Generation)是指在編譯時生成靜態 HTML(可部署至 CDN),避免實時渲染的性能開銷:
Static Generation (Recommended): The HTML is generated at build time and will be reused on each request.
但並不是全部頁面都能在編譯時靜態生成,一種可行的實踐方案是將 SSR 與 Static Generation 結合起來,只對內容依賴個性化數據、或者頻繁更新的頁面走 SSR,其他場景都走 Static Generation:
You should ask yourself: 「Can I pre-render this page ahead of a user’s request?」 If the answer is yes, then you should choose Static Generation.
至此,沉寂多年的 SSR 又煥發出了新的活力
參考資料
What is the point of SSR these days?
Two forms of Pre-rendering
本文分享自微信公衆號 - 前端向後(backward-fe)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。