跳到主要內容
黯羽輕揚每天積累一點點

SSR 與當年的 JSP、PHP 有什麼區別?

免費2020-10-18#Mind#SSG#static site generation#静态渲染#预渲染#服务端渲染

前後端分層之前很長的一段時間裡都是以服務端渲染為主(JSP、PHP),如今兜兜轉轉似乎又回到了起點

寫在前面

SSR(Server-Side Rendering)並不是什麼新奇的概唸,前後端分層之前很長的一段時間裡都是以服務端渲染為主(JSP、PHP),在服務端生成完整的 HTML 頁面

(摘自 前端渲染模式的探索

也就是說,歷經 SSR 到 CSR 的大變革之後,如今又從 CSR 出發去探索 SSR 的可能性……似乎兜兜轉轉又回到了起點,在這之間發生了什麼?如今的 SSR 與當年的 JSP、PHP 又有什麼區別?

一。SSR 大行其道

回到論壇、博客、聊天室仍舊火熱的年代,行業最佳實踐是基於 JSP、PHP、ASP/ASP.NET 的動態網站

以 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](/articles/nginx-https 反向代理/) 之類的 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](/articles/nginx-https 反向代理/) 之類的 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 又煥發出了新的活力

參考資料

評論

暫無評論,快來發表你的看法吧

提交評論