<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          現代前端框架的渲染模式

          共 8964字,需瀏覽 18分鐘

           ·

          2023-06-12 04:26

          React 發(fā)布已經十年了,筆者接觸前端差不多也有十年時間了。說實話,如果沒有 Head First 系列圖書,我可能都沒有走上編程這條道路。

          acdaae6b7c86ac11a96fedab174e2643.webpHead first


          盡管現在看來這系列圖書內容可能過時了。

          Head First 系列圖書讓我知道,原來編程也可以這么通俗易懂的,對于剛接觸這個領域的同學來說,從這里可以獲得很多信心和成就感。這種風格也一直影響著我,學習和工作、傳道授業(yè)過程中,我會努力把復雜的事情簡化、通俗化,提煉本質。


          這十年,前端渲染方式一直在演進,我覺得大概可以分為以下三個階段:

          7434f179c529833ca4680b826268c016.webpUntitled
          • 傳統(tǒng) SSR: 那時候前端還沒有分離,在 JSP、ASP、Ruby on Rails、Django 這些 MVC 框架下,通過模板來渲染頁面。jQuery 是這個階段的主角
          • 前后端分離:從 Node.js 發(fā)布,到目前為止,是前端發(fā)展最迅速的 10 年。前后端分離的典型代表是 Angular 和 React、Vue 等框架,我覺得,促進前后端分離的主要原因還是隨著需求的復雜化,分工精細化了。前端可以專注于 UI 的設計和交互邏輯。后端只需要提供 API,不需要關心前端的具體實現。
          • 同構前端:這幾年前端框架的發(fā)展進入的深水區(qū),隨著云原生、容器技術、Serverless、邊緣計算等底層技術設施的普及,也讓‘前端’生存范圍延展到服務端。前端開始尋求 UXDX 的平衡點

          通過這篇文章,你就可以知道近些年前端渲染模式的演變。廢話不多說,直接開始吧。


          CSR - 客戶端渲染

          6f8e97edebd6f4c610af0dfc82e03c74.webpUntitled

          這個我們再熟悉不過了, 即前端頁面在瀏覽器中渲染,服務端僅僅是靜態(tài)資源服務器(比如 nginx)。

          初始的 HTML 文件只是一個空殼,我們需要等待 JavaScript 包加載和執(zhí)行完畢,才能進行交互,白屏時間比較長。

          • 優(yōu)點
            • 部署簡單
            • 頁面過渡、功能交互友好
            • 適合復雜交互型應用程序開發(fā)
          • 缺點
            • SEO 不友好
            • 白屏時間長
            • 可能需要復雜的狀態(tài)管理。時至今日,狀態(tài)管理方面的輪子還在不停地造

          a1639225bd6761e72378c49a22b2c11f.webp


          SSR - 服務端渲染

          0cf82c5f5a0444dd0187282f6249e956.webpUntitled

          為了解決 SEO 和白屏問題,各大框架開始支持在服務端渲染 HTML 字符串。

          SSR 把數據拉取放到了服務端,因為離數據源比較近,數據拉取的速度會快一點。但這也不是完全沒有副作用,因為需要在服務端等待數據就緒, TTFB(Time to First Byte) 相比 CSR 會長一點。

          SSR 只是給我們準備好了初始的數據和 HTML, 實際上和 CSR 一樣,我們還是需要加載完整的客戶端程序,然后在瀏覽器端重新渲染一遍(更專業(yè)的說是 Hydration 水合/注水),才能讓 DOM 有交互能力。

          也就說, FCP(First Contentful Paint) 相比 CSR 提前了, 但是 TTI(Time to Interactive) 并沒有太多差別。只是用戶可以更快地看到內容了。

          hydration 的主要目的是掛載事件處理器、觸發(fā)副作用等等

          優(yōu)點

          • SEO 友好
          • 用戶可以更快看到內容了

          缺點

          • 部署環(huán)境要求。需要 Nodejs 等 JavaScript 服務端運行環(huán)境
          • 需要包含完整的 JavaScript 客戶端渲染程序,TTI 還有改善空間




          SSG - 靜態(tài)生成

          5c5832b694717b75a822eed18051c614.webpUntitled

          對于完全靜態(tài)的頁面,比如博客,公司主頁等等,也可以使用 SSG 靜態(tài)渲染。

          和 SSR 的區(qū)別是,SSG 是在構建時渲染的。

          和 CSR 一樣,因為是靜態(tài)的,所以在服務端不需要渲染運行時,部署在靜態(tài)服務器就行了。

          VuePress、VitePress、Gatsby、Docusaurus 這些框架都屬于 SSG 的范疇。



          優(yōu)點

          • 相比 SSR, 因為不需要服務端運行時、數據拉取,TTFB/FCP 等都會提前。

          缺點

          • 和 SSR 一樣,也有客戶端渲染程序、需要進行 Hydrate。對于內容為中心的站點來說,實際上并不需要太多交互,客戶端程序還有較大壓縮的空間。
          • 在構建時渲染,如果內容變更,需要重新構建,比較麻煩




          ISG - 增量靜態(tài)生成

          e93e0723c9f3fe93b5c3521c695b48aa.webpUntitled

          ISG 是 SSG 的升級版。解決 SSG 內容變更繁瑣問題。

          ISG 依舊會在構建時預渲染頁面,但是這里多出了一個服務端運行時,這個運行時會按照一定的過期/刷新策略(通常會使用 stale-while-revalidate )來重新生成頁面。



          Progressive Hydration - 漸進水合

          c9c0f5f22420b6d398666a37f1cb3aff.webpUntitled

          上文提到,常規(guī)的 SSR 通常需要完整加載客戶端程序(上圖的 bundle.js),水合之后才能得到可交互頁面,這就導致 TTI 會偏晚。

          最直接的解決辦法就是壓縮客戶端程序的體積。那么自然會想到使用代碼分割(code splitting)技術。漸進式水合 (Progressive Hydration ) 就是這么來的。

          如上圖,我們使用代碼分割的方式,將 Foo、Bar 抽取為異步組件,抽取后主包的體積下降了,TTI 就可以提前了。

          而 Foo、Bar 可以按照一定的策略來按需加載和水合,比如在視口可見時、瀏覽器空閑時,或者交給 React Concurrent Mode 根據交互的優(yōu)先級來加載。

          React 18 官方支持了漸進式水合(官方叫 Selective Hydration)。

          要深入了解 Progress Hydration, 可以看這個視頻。


          SSR with streaming - 流式 SSR

          00a4516898a410086582d3a424bf7650.webpUntitled

          這個很好理解。尤其是在最近 ChatGPT 這么火。ChatGPT API 有兩種響應模式:普通響應、流式響應

          • renderToString → 普通響應。即 SSR 會等待完整的 HTML 渲染完畢后,才給客戶端發(fā)送第一個字節(jié)。
          • renderToNodeStream → 流式響應。渲染多少,就發(fā)送多少。就像 ChatGPT 聊天消息一樣,一個字一個字的蹦,盡管接收完整消息的時間可能差不多,用戶體驗卻相差甚遠。

          瀏覽器能夠很好地處理 HTML 流,快速地將內容呈現給用戶,而不是白屏干等。

          下面這張圖可以更直觀感受兩者區(qū)別:

          6b53fe2a6069a84f23d705243bde94ca.webp來源:https://mxstbr.com/thoughts/streaming-ssr/

          來源:https://mxstbr.com/thoughts/streaming-ssr/

          對于常規(guī)的流式 SSR,優(yōu)化效果可能沒有我們想象的那么明顯。因為框架還是得等數據拉取完成之后才能開始渲染。因此,除非是比較復雜、長序列的 HTML 樹,至上而下需要較長時間的渲染,否則效果并不明顯。

          優(yōu)點

          • 相比普通響應,流式響應可以提前 TTFB 和 FCP, 瀏覽器不用空轉等待,可以連續(xù)繪制。

          缺點

          • 數據拉取是 TTFB/FCP 的主要阻塞原因。為了解決這個問題,下文的 Selective Hydration 如何巧妙地解決這個問題。

          Selective Hydration - 選擇性水合

          93a836ecfa3b4d7ccd244a6d96094833.webpUntitled

          選擇性水合(Progressive Hydration)漸進式水合(Progressive Hydration)流式SSR(SSR with Streaming) 的升級版。主要通過選擇性地跳過‘慢組件’,避免阻塞,來實現更快的 HTML 輸出, 從而讓流式響應發(fā)揮應有的作用。

          慢組件通常指的是:需要異步獲取數據、體積較大、或者是計算量比較復雜的組件。

          比較典型的慢組件是異步數據獲取的組件, 如下圖,未開啟 Selective Hydration 的情況,會等待所有異步任務完成后才開始輸出,而 Selective Hydration 可以跳過這些組件,等待它們就緒后,繼續(xù)輸出。

          ab28ffdd920477b857269764a7407e80.webpUntitled

          我們可以在最新的 Next.js(當前是 13.4) 演示一下。

          沒有開啟 Selective Hydration 的 Demo:
                
                function delay(time: number) {
          return new Promise((resolve) => setTimeout(resolve, time))
          }

          /**
          * 獲取關鍵數據
          */
          function getCrucialData() {
          return delay(1000).then(() => {
          return {
          data: Math.random(),
          }
          })
          }

          function getData(time: number) {
          return delay(time).then(() => {
          return {
          data: Math.random(),
          }
          })
          }

          const Foo = async () => {
          const data = await getData(1000)

          return
          foo: {data.data}

          }

          const Bar = async () => {
          const data = await getData(2000)

          return
          bar: {data.data}

          }

          /**
          * 頁面 ??
          *
          */
          export default async function WithoutSelective() {
          // 獲取關鍵數據
          const crucialData = await getCrucialData()

          return (

          Without Selective


          This page is rendered without Selective Hydration.


          crucial data: {crucialData.data}





          )
          }

          運行結果:瀏覽器等待響應的時間為 3s3202bb1292096d1d9605d8ec661f1b1e.webp即所有服務端組件(Server Component) 就緒后才會有實際的內容輸出。



          開啟 Selective Hydration 很簡單,我們只需要用 Suspend 包裹起來,提示 React 這可能是一個‘慢組件’,可以跳過他:

                
                export default async function WithoutSelective() {
          // 獲取關鍵數據
          const crucialData = await getCrucialData()

          return (

          Without Selective


          This page is rendered without Selective Hydration.


          crucial data: {crucialData.data}









          )
          }

          現在來看運行結果:

          0864a29fcd97f661056842fe3f490985.webpUntitled

          明顯 TTFB 提前了!但是完整的請求時間沒變。


          當 Foo 和 Bar 就緒后,Next.js 會將渲染結果寫入流中。怎么做到的?


          看一眼 HTML 就知道了:

          2b47c27334d1f54a01013c212e201ac8.webpUntitled

          對于慢組件,React 會先渲染 Suspend 的 fallback 內容,并留一個插槽。

          繼續(xù)往下看,可以看到 Foo、Bar 的渲染結果:

          050d03b3a7433b3ba76f593d82978d44.webpUntitled

          接著將渲染結果替換掉插槽。用于后續(xù)的水合。




          總之,在服務端,Selective Hydration 在 SSR With Streaming 的基礎上,通過選擇性地跳過一些低優(yōu)先級的慢組件來優(yōu)化了 TTFB(主要的,相對于 FCP 等指標也優(yōu)化了),更快地向用戶呈現頁面。

          在客戶端 Selective Hydration 的運行過程同 Progressive Hydration 。

          關于 Selective Hydration 細節(jié),可以閱讀以下文章:

          • New in 18: Selective Hydration
          • New Suspense SSR Architecture in React 18

          Islands Architecture - 島嶼架構

          6b6d6e46b5c431b248d012af6516ff51.webpUntitled

          近兩年,去 JavaScript 成為一波小趨勢,這其中的典型代表是 Islands Architecture (島嶼架構)和 React Server Component(RSC, React 服務端組件)。

          它們主張是:在服務端渲染,然后去掉不必要 JavaScript

          島嶼架構的主要代表是 Astro。如上圖,Astro 在服務端渲染后,默認情況下,在客戶端側沒有客戶端程序和水合的過程。而對于需要 JavaScript 增強,實現動態(tài)交互的組件,需要顯式標記為島嶼。


          這有點類似 Progressive Hydration 的意思。但是還是有很大的差別:

          • 島嶼是在去 JavaScript 這個背景下的交互增強手段。按 Astro 解釋是:你可以將‘島嶼’想象成在一片由靜態(tài)(不可交互)的 HTML 頁面中的動態(tài)島嶼
          • 每個島嶼都是獨立加載、局部水合。而 Progressive Hydration 是整棵樹水合的分支,只不過延后了。
          • 島嶼可以框架無關。

          去 JavaScript 后,可以緩解典型的 SSR TTI 問題。但是島嶼架構并不能通吃所有的場景,最擅長的是”內容為中心“的站點,即當靜態(tài)的頁面比重遠高于動態(tài)比重時,去 JavaScript 的收益才是顯著的。




          React Server Component - React 服務端組件

          d10484a756c4ee6b6d81d33729ba7243.webpUntitled

          在筆者看來,React Server Component(RSC) 本質上和島嶼架構的目的是一樣的,都是去 JavaScript。只是實現的手段不同。


          這是 Next.js 官方文檔的示例圖:和島嶼架構類似,對于靜態(tài)的內容推薦使用 Server Component (SC), 而需要交互增強的,可以使用 Client Component (CC)。

          25563bfa30c112edab7906cbba4de05e.webpUntitled

          顧名思義,RSC 就是只能在服務端運行的組件。下面簡單對比一下兩者的區(qū)別:


          Server Component Client Component
          運行環(huán)境 服務端 - 服務端 + 客戶端
          - 僅客戶端

          JavaScript 服務端組件依賴的相關程序對客戶端不可見。
          在這里實現了 ‘去 JavaScript’ 需要打包分發(fā)給客戶端
          水合 不需要水合 需要水合
          支持 async Y N
          支持狀態(tài)(state, context) N Y
          支持事件、副作用 N Y

          RSC 優(yōu)點類似 React Hooks 出來之前的函數組件: 就是一個普通的函數,不能使用 hooks,沒有狀態(tài),只會被調用一次。

          你可以通過 Next.js 的文檔,深入學習 RSC。React 官方的討論組也是不錯的一手學習場地。



          那么相比島嶼架構呢?

          優(yōu)點

          • Server Component 和 Client Component 都是 React 框架的組件,盡管有些區(qū)別,但是心智模型是統(tǒng)一的。
          • React Server Component 是 React 框架下一體化的原生解決方案,支持和 Selective Hydration 配合使用。島嶼架構只是一個架構模式。
          • 可以進行更細粒度和更靈活的組合。

          缺點

          • Server Component 和 Client Component 還是有較大差別,在組合、通信上也有較多限制,需要開發(fā)者規(guī)劃好服務端和客戶端的邊界。初期有一定上手門檻。當然,Islands 可能也有類似的問題。



          總結

          本文篇幅較長,我給大家整理了這些渲染模式的發(fā)展歷程和關系脈絡

          8f5e0843b05d8f4ee7421ebd51132c17.webpUntitled

          任何技術的迭代都是有其動機和脈絡。不推薦大家面向熱度編程,大部分情況下,做到‘知其然,也知其所以然’,就足夠了。




          擴展閱讀

          本文主要參考的內容來源是patterns.dev。這個網站收錄了許多實用的前端設計模式,大家趕緊收藏起來!


          • Pattern dev
          • Next.js
          • Next.js Incremental Static RegenerationExamples
          • reactwg/server-components
          • Is 0kb of JavaScript in your Future?
          • Islands Architecture

          以上便是本次分享的全部內容,希望對你有所幫助^_^

          喜歡的話別忘了 分享、點贊、收藏 三連哦~。

          6da444aed1ee7c65fef6d7d05178a726.webp

          從零搭建全??梢暬笃林谱髌脚_V6.Dooring

          從零設計可視化大屏搭建引擎

          Dooring可視化搭建平臺數據源設計剖析

          可視化搭建的一些思考和實踐

          基于Koa + React + TS從零開發(fā)全棧文檔編輯器(進階實戰(zhàn)




          點個在看你最好看

          瀏覽 127
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产欧美日韩视频在线 | 黄色视屏在线观看 | 亚洲一区二区视频 | 国产青青草偷拍 | 激情做爱网站 |