web頁面渲染(一)
作為開發者,我們經常會面臨一些影響我們整個網站結構的決定,其中web開發者一定要做的核心決定之一就是在應用程式中實現邏輯和渲染的位置。這可能比較難,因為有很多不同的方式來構建一個網站。
我們在這一領域的瞭解主要來源於在過去的幾年在Chrome工作期間,一直與一些大的網站的交流得來的。從廣義上來講,我們鼓勵開發人員去通過完全rehydration方法進行服務端渲染或者是靜態渲染。
為了更好的理解我們所選擇的技術架構,我們需要對每種方法有一種紮實的理解,並且在談論時要使用一致的術語。這些方式的不同點有助於我們說明通過效能和渲染之間尋求一個平衡。
術語
渲染
SSR: Server-Side Rendering - 在伺服器端渲染內容的方式。
CSR: Client-Side Rendering - 通常在瀏覽器中使用DOM來渲染應用程式的過程。
Rehydration: 在客戶端“啟動”Javascript檢視,使得他們能夠重用伺服器端渲染的html的dom樹和資料。
Prerendering: 在構建時執行客戶端應用程式來使用其初始狀態作為靜態的html頁面。
效能
TTFB: Time to First Byte - 點選一個連結到返回的第一個位元組資料所用的時間。
FP: First Paint - 首次任何畫素對使用者可見的時間(也就是首先將畫素繪製到螢幕的時刻)。
FCP: First Contentful Paint - 首次內容對使用者可見的時間(也就是瀏覽器將第一個 DOM 渲染到螢幕的時間)。
TTI: Time To Interactive - 頁面變為可互動的時間。
伺服器端渲染
伺服器端渲染會在伺服器上生成整個的HTML頁面,然後整體返回給瀏覽器。由於它在返回給瀏覽器之前就已經處理好了,所以會避免客戶端在資料獲取和模板化的額外開銷。
伺服器渲染很快就會產生First Paint(FP)和First Contentful Paint(FCP)。在伺服器端執行頁面邏輯和渲染,使之避免了因此而傳送大量的Javascript給客戶端。也會有助於我們實現一個快速的Time To Interactive(TTI)。這是可能的,因為在伺服器端渲染的話,你僅僅需要傳送文字和連結給瀏覽器。這種方式在大範圍的裝置和網路環境下都會很好的執行,與此同時也會提起你對瀏覽器優化的興趣,比如說流文件解析。
通過伺服器端渲染,使用者不太可能等待CPU繫結的javascript處理完畢之後才能去使用您的站點。即使是第三方js不可避免。使用伺服器端渲染也會減少你的js開銷,會給您處理其他事情留有更多的“預算”。然而,這種方式有一個主要的缺點:在伺服器端生成頁面會減慢你的Time To First Byte(TTFB)。
伺服器端渲染對於你的應用程式是否夠用,很大一部分依賴於你個人的構建經驗。在伺服器端渲染和客戶端渲染哪一個更好這個爭論曾經持續了很長時間。但是要記住很重要的一點,你可以在一些頁面中使用伺服器端渲染,而不是全部都用。一些網站已經成功使用了混合渲染技術,Netflix在伺服器端渲染,呈現其相對於靜態的頁面,同時為互動繁重的頁面預取js,給這些較重的客戶端頁面提供一個更快的載入機會。
許多現代瀏覽器,類庫和架構使得在客戶端和伺服器端渲染同一個應用程式成為可能。這些技術可以被用於伺服器端渲染,然而,重要的是注意在客戶端和伺服器端渲染的架構都有他們不同的解決方案,具有非常不同的效能特徵和權衡。React使用者可以使用renderToString()或者是在其之上的解決方案來實現伺服器端渲染,比如說Next.js。Vue使用者可以查閱Vue的server rendering guide或者是Nuxt。Angular有Universal。最流行的解決方案會採用某些形式的hydration。在選擇一個工具之前要注意使用的方法。
靜態渲染
靜態渲染在構建時發生,會提供一個很快的First Paint,First Contentful Paint, Time To Interactive - 假設客戶端js是有限的。跟伺服器端渲染區別之一是它會設法實現一個始終如一的快速的Time To First Byte,因為HTML頁面不必動態生成。通常來說,靜態渲染意味著會提前對每一個URL生成一個單獨的HTML檔案。由於預先生成了HTML的響應,所以靜態渲染可以部署在多個CDN來用於邊緣快取。
靜態渲染的解決方案多種多樣,像Gatsby這樣的工具,開發者會感覺他們的應用程式是動態渲染的,而不是作為構建時生成的。而像Jekly和Metalsmith則會擁抱他們的靜態生態系統,提供更多模板驅動的方法。
靜態渲染的缺點之一就是要為每一個可能的URL單獨生成一個HTML檔案,當你不能提前預知這些URL都有什麼,或者網站有非常多的唯一頁面的時候,這或許是一個挑戰,又或者是不可能實現的。
React使用者可能很熟悉Gatsby,Next.js,static export或者是Navi - 他們都使得作者使用元件變得很方便。然而,重要的是理解靜態渲染和預渲染的不同點:靜態渲染頁面是可互動的,不需要執行太多的客戶端的js,而預渲染則會提升單頁應用的First Paint或者是First Contentful Paint,為了讓頁面變為真正的可互動其一定要在客戶端啟動。
如果你不確定給定的解決方案是靜態渲染,還是預渲染,嘗試下如下測試:禁用Javascript,載入已經建立好的頁面。對於靜態渲染頁面,如果不啟用Javascript,大部分功能依然存在。而對於預渲染頁面,可能依然有一些基本的功能可以使用,比如說超連結,但是大部分頁面將會是惰性的。
另一個有用的測試是使用Chrome DevTool降低你的網路頻寬,觀察頁面變為可互動之前Javascript已經下載的數量。預渲染通常需要更多的Javascript來使頁面變的可互動,並且Javascript要比靜態渲染使用的漸進增強方法要更復雜。
伺服器端渲染 vs 靜態渲染
伺服器端渲染並不是靈丹妙藥 - 它的動態特性會帶來明顯的計算效能開銷。許多伺服器端渲染解決方案都不會提早重新整理,可能會導致延遲TTFB或者是傳送的資料量加倍。在React中,renderToString()可能是很慢的,因為它是同步並且是單執行緒的。要使得伺服器渲染“正確”可能會涉及查詢或者構建一個元件快取的解決方案,管理記憶體消耗,等等。你通常會多次處理/重新構建相同的應用程式 - 一次在客戶端,一次在伺服器端。僅僅是因為伺服器端渲染可以使某些東西更快顯示,但並不是突然意味著會減少你的工作量。
伺服器端渲染可以按需要為每個URL生成對應的HTML,但是它卻慢於靜態渲染的內容。如果你可以進行一些額外的工作,那麼伺服器端渲染 + HTML快取可以極大的減少伺服器渲染的時間。伺服器端渲染的優點是有能力獲取更多“實時”的資料並且返回比靜態渲染更加完整的結果集,個性化的頁面是伺服器渲染的一個具體的事例,不過這種對於靜態渲染來說,就不太適合了。
在構建PWA的時候,伺服器端渲染也可以提出一些有有趣的決策。所以是全頁server worker更好,還是用伺服器渲染僅僅渲染單獨的內容片段更好呢?
本文翻譯自: