(先交代一下背景),我們walmart(沃爾瑪)主站的(大)多數頁面,使用的是服務端渲染(以下用簡稱SSR代替),只有極個別頁面使用的是客戶端渲染。
我們之所以使用服務端渲染,正是基於以下兩個原因:
在使用者購物的過程中,能夠讓使用者享受到頁面效能(的)提升所帶來的使用者體驗
符合(consistent)我們對頁面SEO效能優化的要求
正是因為SSR擁有這些優點,這才讓我們下定決心(對頁面)使用服務端渲染。(不過,說實話),當我們(真的)把技術棧切到React和Nodejs的時候,(吃驚的發現),SSR效能的優化工作(其實)也花了我們很多時間和精力。(為啥呢)?這是因為SSR效能優化的一部分工作是針對首屏渲染的優化,(忘記說了),首屏渲染(above-the-fold render)也是我們衡量效能的重要指標之一。(順道安利一下)我們自己的開源框架Electrode framework,該框架內建了多個有助於SSR效能提升的獨立模組,(如果沒記錯的話),我之前就寫過有關於使用這些模組所帶來效能提升的介紹。
Eletrode框架(旨在解決SSR效能優化問題)一經發布,不久我就收到了許多有關於使用SSR(究竟)能帶來(哪些)好處的提問以及相關的評論。你們現在看的這篇文章,主要講的是使用SSR(究竟)能帶來(哪些)好處,至於SEO優化(究竟)能帶來(哪些)好處等話題,(我覺得)Andrew Farmer和Patrick Hund他倆的文章已經講得很全面啦,(我這裡將不再贅述)。
理論上的效能優點
接下來,我將會用最直觀(super simple)的時序圖(timeline diagram)來闡述SSR以及CSR之間的區別。
從圖中可以看出,(這兩種渲染方式的)區別主要在於出現首屏渲染的時機。對於SSR來說,伺服器返回的是(結構相對完整的)HTML檔案,(通過解析HTML檔案),瀏覽器就能渲染出頁面。而對CSR來說,瀏覽器拿到的只是包含JavaScript程式碼的HTML檔案,(換句話,在瀏覽器渲開始渲染出頁面之前,需要動態建立HTML標籤)。這也就意味著,SSR可以讓瀏覽器在邊下載JavaScript檔案的同時邊渲染HTML頁面,換句話說,瀏覽器再也不需要等到所有的JavaScript檔案下載並執行完之後才去渲染頁面啦。(譯者注:上述方式屬於增量式構建),從SSR以及CSR的時序圖裡,我們可以發現,這兩種渲染方式還是有蠻多共同點的:
- 都需要下載React的
- 都需要經歷虛擬DOM構建過程
- 都需要(給頁面元素)繫結事件來增強頁面的可互動性
不過對於使用SSR方式渲染出的HTML頁面來說,使用者是可以在這些操作(指的是下載React、構建虛擬DOM、繫結事件)完成之前就能看到頁面。再反觀使用CSR方式渲染出的HTML頁面,你必須等到上面的這些操作(指的是下載React、構建虛擬DOM、繫結事件)都完成,virtual-dom轉換成(瀏覽器)頁面上的真實dom之後,使用者才能看到頁面。
使用SSR渲染的另一大優勢(Another Bonus):(熟悉瀏覽器渲染機制的人都應該知道),使用CSR渲染的話,頁面很容易白屏。相反,如果你使用SSR渲染的話,白屏就不(那麼)容易出現啦。儘管大家都知道,使用CSR(在很大程度上)就意味著頁面白屏,不過大多數人還是會使用下面的這種方式來規避(白屏)風險(在伺服器返回所有資料之前,給頁面新增loading圖,然後在所有資料到達之後,把loading圖撤掉)。(譯者注:用磚業術語是醬紫描述的,在資料到達之前,讓頁面開始轉“菊花”,資料到達之後,移除“菊花”圖)
前方高能預警(there are a few caveats),請所有(使用SSR方式)的小夥伴提前做好戰鬥準備:
- 雖說使用SSR方式渲染,不但可以讓瀏覽器更早的渲染出HTML頁面,而且還能讓使用者更快的看到HTML頁面。不過美中不足的是,在React沒有執行之前,這些頁面是不存在互動這一說的,甚至是包括像使用者(快速)點選按鈕等類似(這樣簡單)的互動都不能完成。
(熟悉HTTP協議的人都應該知道),在使用SSR方式渲染HTML頁面的過程中,瀏覽器獲取第一個位元組的時間(Time To First Byte)要長於用CSR渲染HTML頁面所獲取的時間,(為啥呢)?這是因為在你使用SSR方式渲染頁面的過程中,你伺服器需要花更多的時間來渲染出(瀏覽器所需要的)HTML結構,(最後才將渲染好的HTML結構作為響應返回),而不像CSR那樣,伺服器只需要返回位元組相對較少的Json資料(relatively empty respons)。
(通過對比SSR、CSR這兩種渲染方式,你會發現),在使用SSR方式渲染HTML頁面的過程中,伺服器的吞吐量會明顯少於用CSR渲染HTML頁面時伺服器的吞吐量。尤其是當你在服務端使用react的時候,(你會發現,是否使用react的服務端渲染特性,伺服器吞吐量往往也是我們考慮的因素),這是因為react對伺服器吞吐量的影響太大啦(the throughput impact is extremely large)。
ReactDOMServer.renderToString
具有以下特點:同步方法
(屬於CPU獨享型),在呼叫過程中,會繫結CPU
會阻塞(hold)整個事件迴圈流程
(換句話說),在ReactDOMServer.renderToString
沒有執行完之前,伺服器是(絕)不可能處理其它請求的。(啥?你說我講的太抽象啦,完全聽不懂),(那好吧,不妨)讓我們做個假設(Let’s say ),在使用SSR渲染HTML頁面的過程中,執行ReactDOMServer.renderToString
就花了500ms,這意味你現在每秒最多隻能處理兩個請求。(如果有同學對這方面感興趣的話,可以重點關注一下)
真實案例(Real Use Case)
(接下來,我將會帶著大家來親身感受一下,我們walmart主站是如何在生產環境實踐SSR以及CSR的),以下是我們針對SSR、CSR這兩種渲染方式所做的一些對比。
為了(更好的)對比SSR、CSR這兩種渲染方式,我們分別拿出了(walmart主站的)home、category以及search三個模組來進行對比。(忘記說了),當前的這張圖片以及接下來的圖片都是我們在頁面進行渲染的過程中,使用chrome(瀏覽器自帶的Screenshots工具)所抓取的快照。(譯者注:此處應該有掌聲),通過檢視圖片,你會發現使用SSR的話,HTML頁面渲染更快,相反使用CSR的話,HTML頁面在載入過程中會出現白屏。雖說絕大多數應用還是可以通過(顯式地使用)loading圖的方式來解決頁面白屏,不過我們的做法是醬紫的:在常規情況下,我們會使用SSR方式,(不過一旦 生產環境出現緊急情況),需要強制切到CSR的情況下,頁面還是會出現白屏。請注意,雖說上面的圖片只是(對HTML頁面渲染情況的)一次快照,(不過大家應該不用擔心),因為我們每天都會定期在不同的時間點、不同的生產環境來跑我們的機器,所以說我們得到的個體樣本資料非常全(vary-but),(全到啥子程度呢)?你都可以從這些資料中看出app接下來的效能走勢啦。
我們可以看到,(在分別使用SSR、CSR這兩種渲染方式的情況下),伺服器會針對home、category以及search頁面返回相對應的響應頭資料。不過我會綠色bar忽略掉,這是因為在網路拓撲圖(network graph)當中,(我們發現),相比綠色bar位置之外的資料來說,綠色bar所反映出的資料更具有相對性(more relative)。(在這些資料當中),我比較關注的是檔案大小以及TTFB。既然伺服器會針對不同的頁面返回相對應的HTML,(那麼你就有可能注意到),使用SSR方式渲染出的HTML體積總是比用CSR方式大。另外一點就是關於伺服器的響應速度,(經過我們測試發現),使用CSR方式,能夠讓伺服器更快地響應頁面請求(由於其它原因,在請求home page頁面的過程中,伺服器響應的速度反而變慢啦)。
我不能完全保證上述結論的正確性,這是因為上述結論摻雜了很多不穩定的因素比如網路是否延遲,伺服器是否正常,訪問者的地理位置以及其它因素。所以希望大家不要把上述結論理解為真理(scientific fact),更多的是把它理解為對SSR以及CSR這兩種渲染方式趨勢的一種解讀。
Electrode
當我們分別對CSR、SSR這兩種渲染方式進行A/B測試時,發現(我們看到的)結果和之前預期的一樣,另外我們的統計資料顯示,渲染的越早,使用者的參與度就會越高。
正是基於以上原因,我們才決定把開源electrode平臺的關注點放在SSR上。electrode預設開啟SSR,另外我們有一些模組是圍繞優化SSR展開的。在另外一篇文章中,我將會為大家展示,如何使用這兩個模組,將RenderToString()的時間縮減至30%。