新一代Web技術棧的演進:SSR/SSG/ISR/DPR都在做什么?


新一代Web技術棧的演進:SSR/SSG/ISR/DPR都在做什么?

Stark 騰訊技術工程 2021-05-10
在開始閱讀之前,先解釋一下文章里用到的英文縮寫:
  • CSR:Client Side Rendering,客戶端(通常是瀏覽器)渲染;

  • SSR:Server Side Rendering,服務端渲染;

  • SSG:Static Site Generation,靜態網站生成;

  • ISR:Incremental Site Rendering,增量式的網站渲染;

  • DPR:Distributed Persistent Rendering,分布式的持續渲染。

 

從 SSR 到 SSG

SSR 這套技術棧相信很多人應該都非常熟悉了(如果你不熟悉的話可以先閱讀相關文章),React/Vue/Angular 等等都從框架層面直接提供了支持,例如在 React 中你可以這樣使用:
import React from 'react'import ReactDOMServer from 'react-dom/server'
const html = ReactDOMServer.render(<h1>Hello, world!</h1>)

SSR 最早是為了解決單頁應用(SPA)產生的 SEO、首屏渲染時間等問題而誕生的,在服務端直接實時同構渲染用戶看到的頁面,能最大程度上提高用戶的體驗,流程類似下面: 圖片但 SSR 引入了另一個問題,既然要做服務端渲染,就必然 需要一個實時在線的后台服務(通常是基於 Node.js 的服務)用來承載頁面請求,那么:1、 需要服務器的計算資源和公網流量來部署這套服務,並且消耗的資源與頁面的訪問量成正相關,當頁面的訪問量突增時,渲染服務也需要進行擴容;2、服務端只能部署在有限的幾個地域,對於距離服務端較遠的用戶而言, 加載速度跟靜態資源的 CDN 相比,慢了一個數量級(通常是 1-5ms VS 50-100+ms);3、日常也存在傳統服務端同樣的運維、監控告警等方面的負擔,團隊 需要額外的人力來開發和維護
有沒有辦法解決這些問題呢?我們重新對 SSR 進行審視,服務端渲染出的頁面,邏輯上講可以分成下面兩大塊:1、 變化不頻繁,甚至不會變化的內容:例如文章、排行榜、商品信息、推薦列表等等,這些數據非常適合緩存;2、 變化比較頻繁,或者千人千面的內容:例如用戶頭像、Timeline、登錄狀態、實時評論等。例如,在一篇文章的頁面中,文章的主題內容是偏向於靜態的,很少有改動,那么每次用戶的頁面請求,都通過服務端來渲染就變得非常不值得, 因為每次服務端渲染出來大部分內容都是一樣的!我們完全可以將文章的頁面渲染為靜態頁面,至於頁面內那些動態的內容(用戶頭像、評論框等),就通過 HTTP API 的形式進行瀏覽器端渲染(CSR): 圖片這樣做有很多好處:1、由於文章內容已經被靜態化了,所以它是 SEO 友好的,能被搜索引擎輕松爬取;2、大大減輕了服務端渲染的資源負擔,不需要額外做一套 Node.js 服務;3、用戶始終通過 CDN 加載頁面核心內容,CDN 的邊緣節點有緩存,速度極快;4、通過 HTTP API + CSR,頁面內次要的動態內容也可以被很好地渲染;5、數據有變化時,重新觸發一次網站的異步渲染,然后推送新的內容到 CDN 即可。6、由於每次都是全站渲染,所以網站的版本可以很好的與 Git 的版本對應上,甚至可以做到原子化發布和回滾。這便是 Gatsby.js、Next.js 這樣的網站生成器解決的問題,他們屬於 React/Vue 更上一層的框架(Meta Framework),通過 SSR 把動態化的 Web 應用渲染為多個靜態頁面,並且對高度動態的內容也保留了 CSR 的能力。

從 SSG 到 ISR/DPR

細心的同學一定發現了 SSG 這樣的模式,看似美好,但存在一個瑕疵:對於只有幾十個頁面的個人博客、小型文檔站而言,數據有變化時,跑一次全頁面渲染的消耗是可以接受的。 但對於百萬級、千萬級、億級頁面的大型網站而言,一旦有數據改動,要進行一次全部頁面的渲染,需要的時間可能是按小時甚至按天計的,這是不可接受的。為了解決這個問題,各種框架和靜態網站托管平台都提供了不同的方案,這里我們介紹 ISR 和 DPR 兩種。

ISR:

Incremental Site Rendering

既然全量預渲染整個網站是不現實的,那么我們可以做一個切分:1、 關鍵性的頁面(如網站首頁、熱點數據等)預渲染為靜態頁面,緩存至 CDN,保證最佳的訪問性能;2、 非關鍵性的頁面(如流量很少的老舊內容)先響應 fallback 內容,然后瀏覽器渲染(CSR)為實際數據;同時對頁面進行異步預渲染,之后緩存至 CDN,提升后續用戶訪問的性能。 圖片頁面的更新遵循 stale-while-revalidate 的邏輯,即始終返回 CDN 的緩存數據(無論是否過期);如果數據已經過期,那么觸發異步的預渲染,異步更新 CDN 的緩存。 圖片這就是增量式更新(ISR)的概念,這個概念最早由 Next.js 在 9.5 版本中提出,下面是一個小 Demo: Static Reactions Demo: https://reactions-demo.vercel.app/在 Next.js 中,你可以使用 getStaticPaths() 來定義哪些路徑需要預渲染,通過 getStaticProps() 來獲取預渲染需要的數據:
// 定義哪些頁面需要預渲染export async function getStaticPaths() { return { // 只有 /posts/1 和 /posts/2 會被預渲染 paths: [{ params: { id: '1' } }, { params: { id: '2' } }], // 其它頁面,如 /posts/3,都會返回 fallback 頁面,然后 CSR fallback: true, }}
// 定義預渲染需要的數據export async function getStaticProps({ params }) { // 拉取對應的文章內容 const res = await fetch(`https://.../posts/${params.id}`) const post = await res.json()
return { props: { post }, revalidate: 60 // 數據有效期為 60 秒 }}
但 ISR 存在部分缺陷:1、對於沒有預渲染的頁面,用戶首次訪問將會看到一個 fallback 頁面,此時服務端才開始渲染頁面,直到渲染完畢。這就導致用戶 體驗上的不一致。2、對於已經被預渲染的頁面,用戶直接從 CDN 加載, 但這些頁面可能是已經過期 的,甚至過期很久的,只有在用戶刷新一次,第二次訪問之后,才能看到新的數據。對於電商這樣的場景而言,是不可接受的(比如商品已經賣完了,但用戶看到的過期數據上顯示還有)。
具體關於 ISR 的利弊,可以進一步看 Netlify 的這篇文章:Incremental Static Regeneration: Its Benefits and Its Flaws:https://www.netlify.com/blog/2021/03/08/incremental-static-regeneration-its-benefits-and-its-flaws/

DPR:

Distributed Persistent Rendering

為了解決 ISR 的一系列問題,Netlify 在前段時間發起了一個新的提案:
Distributed Persistent Rendering (DPR)https://github.com/jamstack/jamstack.org/discussions/549DPR 本質上講,是對 ISR 的模型做了幾處改動,並且搭配上 CDN 的能力:
1、去除了 fallback 行為,而是直接用 On-demand Builder(按需構建器)來響應未經過預渲染的頁面,然后將結果緩存至 CDN;2、數據頁面過期時,不再響應過期的緩存頁面,而是 CDN 回源到 Builder 上,渲染出最新的數據;3、每次發布新版本時,自動清除 CDN 的緩存數據。 圖片在 Netlify 平台上,你可以像這樣定義一個 Builder,用於預渲染或者實時渲染。這個 Builder 將會以 Serverless 雲函數的方式在平台上運行:
const { builder } = require("@netlify/functions")
async function handler(event, context) {
return { statusCode: 200, headers: { "Content-Type": "text/html", }, body: ` <!DOCTYPE html> <html> <body> Hello World </body> </html> `, };}
exports.handler = builder(handler);
更多詳細信息可以參考文檔:https://docs.netlify.com/configure-builds/on-demand-builders/
當然 DPR 還在很初期的階段,就目前的討論來看,依然有一些問題:
  • 新頁面的訪問可能會觸發 On-demand Builder 同步渲染,導致當次請求的響應時間比較長;

  • 比較難防御 DoS 攻擊,因為攻擊者可能會大量訪問新頁面,導致 Builder 被大量並行地運行,這里需要平台方實現 Builder 的歸一化和串行運行。


總結

Jamstack 這套技術棧在國外的流行,很大程度上得益於近年來相關雲服務和雲平台的成熟:
  • 新一代的 CDN 技術,包括更高級、更精細的緩存控制能力;

  • Serverless形態的計算服務(如雲開發CloudBase提供的雲函數與雲托管功能),讓 SSR 和 SSG 免於服務器運維的苦惱,開發者只需要重點關注前台邏輯;
  • 越來越豐富的 BaaS 提供方,提供了包括數據存儲、鑒權、電商、CMS、音視頻、AI 等等“中台化”的能力,開發者只需要組合這些 BaaS 服務,專注於自身的業務邏輯即可。

     

Jamstack 非常適合以呈現內容為主的網站,如文檔、博客、電商網站、論壇、官網等等,所以更多地應該將它視為“建站技術”,是目前諸多建站技術棧(LAMP、MEAN等等)的一個新生替代品。極低的運維成本、Serverless、快速、安全、且不損失網站的動態性,是它的核心優勢。當然它本身並不是完美的,SSG、ISR、DPR 這些解決方案,都或多或少有一些瑕疵和問題,它們本質上就是在平衡動態性、渲染性能、緩存性能這三個矛盾點,依然需要繼續探索和演進下去。另外,除了上文提到的 Netlify 和 Vercel 這兩家小而美的平台以外,國外的幾家大型雲廠商(GCP、AWS、Azure)也提供了類似的產品,向 Web 前端開發者提供對 Jamsatck 等新生代技術棧的支持:
  • Firebase Hosting

  • Azure Static Web Apps

  • AWS Amplify

國內市場上,這塊產品目前還處於缺位的狀態,雖然底層的 IaaS 能力(對象存儲、CDN、Serverless、網關等等)都趨近於完善,但還缺少能夠把這些能力組合封裝起來的一層,前段時間我也表達過類似的想法:
“經常看到有人討論為啥國內沒有netlify、vercel這樣的web托管產品,其實在國內要做個類似的東西還真沒那么容易,起碼要打通 CDN、對象存儲、Serverless、API網關,正式產品化的話還要考慮計費、安全、用戶隔離等等,能完整提供這套基建的國內廠商就AT兩家,並且邊邊角角上還缺了挺多能力的。”
當然除了技術層面的原因外,國內外的市場、網絡環境、技術生態都是完全不同的,僅僅是 “Copy to China” 的方式很可能會導致產品水土不服,不過這就超出本文的范疇了,可以后續安排一篇文章詳細聊聊。- End -作者:Stark,雲開發 CloudBase 團隊高級前端工程師,Node.js Core Collaborator,知乎 JavaScript 話題優秀答主。
 
 
 
 
 
 
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM