摘要:由我所在的團(tuán)隊共同翻譯完成,并發(fā)布在前端技術(shù)公眾號方凳雅集上,轉(zhuǎn)載于此。在移動端,客戶端渲染很難獲得并保持一個較快的渲染速度。使用技術(shù)進(jìn)行服務(wù)端渲染的主要問題在于它會對可交互時間有明顯的負(fù)面影響,盡管它縮短了首次繪制時間
本文簡單介紹了web應(yīng)用各種渲染方案,其中包括客戶端渲染、服務(wù)器端渲染等各種渲染方案。文章翻譯自:https://developers.google.com...。由我所在的團(tuán)隊共同翻譯完成,并發(fā)布在前端技術(shù)公眾號:方凳雅集上,轉(zhuǎn)載于此。方凳雅集是阿里CBU前端技術(shù)專業(yè)號,有興趣的小伙伴可以關(guān)注一發(fā)。1. 起航
作為開發(fā)人員,我們經(jīng)常面臨影響應(yīng)用程序整個架構(gòu)的決策。我們必須做出的核心決策之一是在什么地方實(shí)現(xiàn)業(yè)務(wù)邏輯和渲染邏輯。這可能很困難,因?yàn)橛泻芏嗖煌姆椒▉順?gòu)建網(wǎng)站。對這個領(lǐng)域的理解來自于過去幾年我們在一些大型網(wǎng)站的工作。從廣義上說,我們鼓勵開發(fā)人員選用帶有rehydration(下一小節(jié)有解釋)的服務(wù)器端渲染或靜態(tài)化渲染。為了更好理解我們在做決定時所選擇的架構(gòu),需要對每種方法和術(shù)語有充分的理解,通過不同渲染方式的頁面性能可以幫助我們理解它們之間的差異。
2. 術(shù)語渲染:
SSR:服務(wù)器端渲染。
CSR:客戶端渲染。
Rehydration:在服務(wù)器端渲染的dom樹和數(shù)據(jù)的基礎(chǔ)上,瀏覽器端利用JavaScript再次渲染。
Prerendering:在構(gòu)建時生成靜態(tài)HTML和頁面的初始狀態(tài)。性能:
TTFB:Time to First Byte —— 瀏覽器發(fā)出資源請求到接受到資源第一個字節(jié)的時間。
FP:First Paint —— 頁面打開到可視內(nèi)容第一個像素渲染出來的時間。
FCP:First Contentful Paint —— 頁面打開到頁面主要內(nèi)容可見的時間。
TTI:Time To Interactive —— 頁面打開到變得可以交互的時間。
這里只是簡單的介紹了這些性能術(shù)語,想要詳細(xì)的了解它們,可以看一下我們之前寫的兩篇文章性能優(yōu)化篇——以用戶為中心的指標(biāo)(一)和性能優(yōu)化篇——以用戶為中心的指標(biāo)(二)。
3. 服務(wù)器端渲染服務(wù)器端渲染:打開頁面時服務(wù)器將完整的HTML生成好了并返回。這避免了在瀏覽器端取數(shù)然后渲染所產(chǎn)生的消耗,因?yàn)檫@些事情已經(jīng)在服務(wù)器端響應(yīng)用戶之前就已經(jīng)做好了。
服務(wù)器端渲染會帶來快速的FP和FCP,在服務(wù)器端處理業(yè)務(wù)邏輯和渲染邏輯,可以避免向?yàn)g覽器發(fā)送大量JavaScript,這有助于實(shí)現(xiàn)快速的TTI,這種方法適用于各種設(shè)備和網(wǎng)絡(luò)環(huán)境,如果你開啟了一些流瀏覽器優(yōu)化,比如文檔采用流的方式解析(streaming document parsing)。
對于服務(wù)器端渲染,用戶不太可能會去等瀏覽器執(zhí)行其他的耗CPU的JavaScript代碼執(zhí)行完畢再去操作,因?yàn)轫撁鎯?nèi)容已經(jīng)顯示出來了。在第三方j(luò)s無法避免時(廣告),雖然服務(wù)器端渲染可以減少FP和FCP渲染的JavaScript消耗,但是可能會給接下來要執(zhí)行的js帶來一定的“負(fù)擔(dān)”。服務(wù)器端渲染的主要缺點(diǎn)在于,渲染需要消耗時間,所以可能TTFB會比較大。
服務(wù)器渲染是否可以滿足應(yīng)用程序,很大程度上取決于您正在構(gòu)建的體驗(yàn)類型。關(guān)于服務(wù)器渲染與客戶端渲染的哪個更好的爭論從未停過。但是我們需要記住的時,我們可以有選擇讓一些頁面進(jìn)行服務(wù)器端渲染,其他頁面使用客戶端渲染。一些網(wǎng)站使用這種混合渲染模式就取得了不錯的效果,比如Netflix對他的登錄頁面采用了服務(wù)器端渲染,同時為重交互的頁面預(yù)取js,為那些重交互并且客戶端渲染的頁面加快頁面加載的機(jī)會。
許多現(xiàn)代框架、庫和架構(gòu)使得同一個應(yīng)用同時在服務(wù)器端和瀏覽器端都能渲染成為可能。這些技術(shù)雖然可以用于服務(wù)器端渲染,但需要注意的是,在服務(wù)器和客戶端上進(jìn)行渲染的體系結(jié)構(gòu)具有非常不同的性能特征和權(quán)衡。React用戶可以用它的renderToString方法或者其他基于該方法的框架進(jìn)行服務(wù)器端渲染,比如Next.js; Vue用戶可以去看它的服務(wù)器端渲染指南或者Nuxt; Angular用戶可以去看Universal。
4. 靜態(tài)化渲染靜態(tài)化渲染:在構(gòu)建(build)時將頁面中不會變化的內(nèi)容直接渲染成出來,然后打到HTML中去。在瀏覽器端需要執(zhí)行的js有限的假設(shè)下,該方法能夠提供快速的FP、FCP和TTI。與服務(wù)器端渲染不同,它還能提供快速的TTFB,因?yàn)榉?wù)器端不需要生成HTML。一般而言,靜態(tài)化渲染需要為每個URL生成一個多帶帶的HTML,當(dāng)用戶訪問的時候,直接將預(yù)先渲染好的HTML返回就好。另外渲染出的HTML也可以部署到CDN上,通過edge caching(邊緣緩存)緩存做一些優(yōu)化。對于不了解edge caching的同學(xué),可以去看一下我們之前寫的React緩存小記,里面有關(guān)于它的介紹。
靜態(tài)化渲染也有不同的方案,比如像Gatsby這樣的工具旨在讓開發(fā)人員感覺他們的應(yīng)用程序是動態(tài)渲染的,而不是在構(gòu)建過程中產(chǎn)生靜態(tài)的HTML;像Jekyl和Metalsmith這樣的工具擁抱的靜態(tài)特性,提供了很多模板驅(qū)動的方法。
靜態(tài)化渲染需要為每個URL生成多帶帶的HTML,這是它的一個缺點(diǎn)。如果您無法提前預(yù)測這些URL的內(nèi)容,或者或一個網(wǎng)站存在大量的URL,靜態(tài)化渲染可能是不合適的。對于靜態(tài)化渲染,React用戶可能對Gatsby、Next.js的static export或者Navi比較熟悉。
這里我們需要重點(diǎn)理解一下靜態(tài)化渲染和預(yù)渲染之間的差異:靜態(tài)化渲染的頁面在瀏覽器端變得能夠交互之前只需要執(zhí)行很少的js代碼,甚至不需要;而預(yù)渲染雖然能夠?qū)崿F(xiàn)快速的FP、FCP,但應(yīng)用必須在瀏覽器端執(zhí)行js代碼才能變得可交互。
如果您不能確定到底是使用靜態(tài)化渲染還是預(yù)渲染,那就測試一下唄,在應(yīng)用加載的時候,禁止JavaScript的加載和執(zhí)行。禁止JavaScript后,對于靜態(tài)化渲染,頁面的大多數(shù)功能還是能夠正常運(yùn)行;而對于預(yù)渲染,頁面可能只有一些基礎(chǔ)的功能能夠工作,比如連接跳轉(zhuǎn)。
另一個有用的測試方式是通過Chrome的開發(fā)者工具DevTools減慢網(wǎng)絡(luò)速度,觀察在頁面變?yōu)榻换ブ跋螺d了多少JavaScript。預(yù)渲染通常需要更多的JavaScript,并且復(fù)雜度往往也比使用漸進(jìn)增強(qiáng)的靜態(tài)化渲染要高。
5. 服務(wù)器端渲染VS靜態(tài)化渲染服務(wù)器端渲染不是一顆銀彈,它的動態(tài)特性會帶來巨大的計算開銷。服務(wù)器端渲染會加大TTFB或發(fā)送雙倍的數(shù)據(jù)(比如將客戶端的State數(shù)據(jù)打到HTML中去),在React中,renderToString會很慢,因?yàn)樗峭讲⑶覇尉€程的。為了讓服務(wù)器端渲染能夠”正確“運(yùn)行,我們需要關(guān)注組件緩存、內(nèi)存消耗、memoization技術(shù)應(yīng)用和其他問題。通常情況你可能需要多次渲染同一個應(yīng)用——一次在服務(wù)器端,一次在客戶端。服務(wù)器端渲染只能讓需要展示的內(nèi)容顯示的的更快,并沒有減少工作量。
服務(wù)器端渲染可以根據(jù)不同的URL生成不同的內(nèi)容,相較于靜態(tài)化渲染,它的速度可能會慢一些。其實(shí)我們可以做一些工作來緩解這個問題,服務(wù)器渲染 + HTML緩存可以大大減少渲染時間。服務(wù)器端渲染的優(yōu)勢在于它能夠獲取實(shí)時數(shù)據(jù),并且所能處理的請求集比靜態(tài)化渲染要大。因?yàn)殪o態(tài)化渲染只能處理那些提前能夠預(yù)測內(nèi)容的頁面,對于需要個性化的頁面(千人千面),靜態(tài)化渲染就無法處理了。
在構(gòu)建PWA時,服務(wù)器端渲染也有用武之地,服務(wù)器端對頁面片段進(jìn)行渲染,前端使用Service Worker進(jìn)行緩存。
6. 客戶端渲染客戶端渲染:是指在瀏覽器中直接使用JavaScript來渲染頁面,所有的邏輯、數(shù)據(jù)的獲取、模板和路由都是在客戶端而不是服務(wù)器上處理的。
在移動端,客戶端渲染很難獲得并保持一個較快的渲染速度。有時我們只需要做很少的工作,就能讓客戶端渲染的性能與服務(wù)器端渲染的性能相差無幾,比如盡可能的保持小的JS體積和少的RTT(https://en.wikipedia.org/wiki...)。甚至關(guān)鍵的腳本和數(shù)據(jù)如果使用HTTP/2的服務(wù)器端推送,或者使用,我們還可以讓解析工作更早開始。另外,像PRPL這樣的技術(shù)也可以幫助我們加快頁面的初始化及其后續(xù)導(dǎo)航。
但事情并不是這么簡單??蛻舳虽秩镜闹饕獑栴}是,所需的JavaScript會隨著應(yīng)用程序的增長而增長。隨著新的JavaScript庫、兼容組件和第三方代碼的添加,控制腳本的規(guī)模會變得格外困難——尤其是這些代碼和庫都經(jīng)常都需要在頁面內(nèi)容渲染之前被加載。所以對于那些腳本規(guī)模很大的應(yīng)用,應(yīng)該優(yōu)先考慮使用代碼拆分的方案。特別的,對于懶加載的JavaScript,要確保只在有需要時才加載必要的代碼。而對于只有很少或者沒有什么交互的應(yīng)用,服務(wù)器渲染是一個可擴(kuò)展性更好的方案。
如果你想構(gòu)建SPA(單頁)應(yīng)用,使用app shell緩存頁面中交互的核心組件會給你很大的幫助。如果結(jié)合PWA的Service Work技術(shù),還可以有效提高頁面重復(fù)訪問時的性能。
7. 通過rehydration將服務(wù)器渲染和客戶端渲染相結(jié)合通常稱為同構(gòu)渲染或者直接簡單地稱為"SSR",這種方式嘗試在客戶端渲染和服務(wù)端渲染之間尋找平衡,希望能夠減少兩者的弊端。
頁面導(dǎo)航導(dǎo)致跳轉(zhuǎn)或刷新時,服務(wù)器會輸出頁面的HTML文檔,并把該頁面所需要的javascript和(用于渲染的)數(shù)據(jù)內(nèi)聯(lián)到文檔一起輸出。如果實(shí)現(xiàn)得當(dāng),這種方式確實(shí)可以像服務(wù)端渲染那樣實(shí)現(xiàn)較快的首次內(nèi)容繪制(First Contentful Paint),之后客戶端會通過一種叫rehydration的技術(shù)繼續(xù)(在客戶端)渲染。這是個新穎的技術(shù),但它會引起比較大的性能問題。
使用reydration技術(shù)進(jìn)行服務(wù)端渲染的主要問題在于它會對可交互時間(Time To Interactive)有明顯的負(fù)面影響,盡管它縮短了首次繪制時間(First Paint)。服務(wù)端渲染的頁面往往讓人感覺已經(jīng)加載完畢并可以開始交互了,但實(shí)際上只有等到客戶端的js腳本執(zhí)行并完成DOM事件綁定才能響應(yīng)用戶的交互(例如用戶的輸入行為)。在一些手機(jī)終端,這個過程會耗費(fèi)幾秒甚至幾分鐘的時間。
也許你自己也經(jīng)歷過這樣的場景:一個頁面看起來已經(jīng)加載完成了,但是在頁面執(zhí)行點(diǎn)擊或者輕觸的動作,結(jié)果卻什么也沒發(fā)生。這很快變得令人沮喪......“為什么(頁面)沒有反應(yīng)?為什么我不能滾動?”
Rehydration問題:重復(fù)
Rehydration的問題不止于此,通常比因js導(dǎo)致的交互延遲更糟糕。為了讓客戶端js能夠準(zhǔn)確地渲染,而不用重新向服務(wù)器請求渲染所需的數(shù)據(jù),目前服務(wù)端渲染通常會把UI所需的數(shù)據(jù)序列化并內(nèi)聯(lián)到HTML文檔的script標(biāo)簽里。最終的HTML文檔包含了更高層面的重復(fù):
可以看到,對于頁面導(dǎo)航請求,服務(wù)器會返回了相應(yīng)的UI描述(HTML),但它同樣返回了渲染UI所需的數(shù)據(jù)。同時,客戶端腳本同樣包括了UI的描述(譯者注:前端渲染需要包含對UI的描述,例如JSX),以便在客戶端繼續(xù)渲染。只有當(dāng)bundle.js完成下載和執(zhí)行后,UI才進(jìn)入可交互狀態(tài)。
從使用rehydration方案的一些真實(shí)網(wǎng)站搜集到的性能數(shù)據(jù)來看,該方案是極度不推薦的。究其原因,還是回到用戶體驗(yàn)上:這種方式很容易讓用戶停留在“神秘的峽谷”之中。(譯者注:即界面可見但不可交互的狀態(tài))
盡管如此,外界對rehydration方案還是有些許期待的。簡單來說,使用服務(wù)端渲染時,只對需要高度緩存的內(nèi)容才會降低首字節(jié)時間(TTFB),得到和預(yù)渲染(prerendering)類似的結(jié)果。
在未來,rehydtration方案可能會逐漸地、或者部分地被應(yīng)用,并成為服務(wù)端渲染的關(guān)鍵。
8. 流式服務(wù)端渲染和漸進(jìn)式Rehydration服務(wù)端渲染在過去幾年中有了長足的進(jìn)展。
流式服務(wù)端渲染允許你以塊的形式發(fā)送HTML,同時瀏覽器端接收并逐一渲染,這種方案可以實(shí)現(xiàn)快速的FP和FCP。React提供了一個異步的、以流的方式傳輸?shù)姆绞?renderToNodeStream,與同步的renderToString相比,它能夠更好處理服務(wù)器壓力。
漸進(jìn)式Rehydration也值得關(guān)注,React團(tuán)隊正在做一些有趣的探索(https://github.com/facebook/r...)。使用這種方法,服務(wù)器渲染隨著時間的推移被“啟動”去渲染應(yīng)用的各個片段,而不是當(dāng)前一次性渲染整個應(yīng)用。這可以幫助減少使頁面交互所需的JavaScript,因?yàn)檫@樣可以延遲頁面中低優(yōu)先級展示內(nèi)容的渲染(比如非首屏的內(nèi)容),同時可以防止這些低優(yōu)先級的渲染阻塞主線程。而且,這種方案也能避免帶Rehydration的服務(wù)端渲染的一個很大的陷阱:服務(wù)端渲染生成的DOM樹在瀏覽器端被銷毀然后被重建,這個問題大多數(shù)是因?yàn)橥降目蛻舳虽秩旧蒁OM樹所需要的初始數(shù)據(jù)還沒準(zhǔn)備好。
局部Rehydration
局部rehydration被證明難以實(shí)現(xiàn)。這個方案是漸進(jìn)式Rehydration的一個擴(kuò)展,需要分析出相互獨(dú)立的片段(組件/視圖/樹)中哪些具有極少交互或者完全沒有交互的部分進(jìn)行漸進(jìn)式rehydrated。對于這些近乎靜態(tài)的部分,相應(yīng)JavaScript代碼會被改造成惰性的引用,從而將它們對客戶端的影響降低到近乎為0。局部hydration為緩存帶來了一定挑戰(zhàn),客戶端導(dǎo)航意味著我們不能假設(shè)應(yīng)用程序的惰性部分即服務(wù)器渲染生成的HTML是可用的,除非頁面完全加載完。
Trisomorphic渲染
如果Service Worker是你的一個選項(xiàng),“trisomorphic”渲染也是一個有趣的點(diǎn)子。利用這項(xiàng)技術(shù),你可以利用流式服務(wù)端渲染生成初始的或不依賴JS的部分,然后在Service Worker install后利用它渲染生成html。這種方案能使緩存的組件和模板保持實(shí)時而且還支持SPA類型的應(yīng)用,在同一會話中根據(jù)不同的導(dǎo)航渲染不同的視圖。這種方法在服務(wù)器端、客戶端和Service Worker中可以復(fù)用模板和路由代碼。
在選擇在渲染方案,通常會考慮SEO。通常我們會選擇服務(wù)器渲染來應(yīng)對爬蟲。爬蟲可能能理解JavaScript,但是它們有很多局限性,我們需要重點(diǎn)關(guān)注一下他們是如何渲染的。雖然客戶端渲染可以工作,但是一般都沒有做測試等工作,如果您的應(yīng)用依賴于客戶端渲染,動態(tài)渲染是您值得考慮的一個選項(xiàng),具體可以參考https://developers.google.com...。
如果有疑問,Mobile Friendly Test可以測試您選擇的方法是否符合預(yù)期,它可以顯示頁面在爬蟲中的顯示方式、序列化的HTML內(nèi)容(JavaScript執(zhí)行之后)以及渲染過程中出現(xiàn)的任何錯誤。
Mobile Friendly Test地址如下:
https://search.google.com/tes...
當(dāng)決定用什么方式渲染的時候,要知道我們即將遇到的瓶頸是什么。采用客戶端渲染還是服務(wù)端渲染將決定你90%的架構(gòu)設(shè)計。一個完美的解決方案通常服務(wù)端發(fā)送html跟最小的js來完成交互。以下是服務(wù)端-客戶端渲染的一個總結(jié)圖:
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/53802.html
摘要:由我所在的團(tuán)隊共同翻譯完成,并發(fā)布在前端技術(shù)公眾號方凳雅集上,轉(zhuǎn)載于此。在移動端,客戶端渲染很難獲得并保持一個較快的渲染速度。使用技術(shù)進(jìn)行服務(wù)端渲染的主要問題在于它會對可交互時間有明顯的負(fù)面影響,盡管它縮短了首次繪制時間 本文簡單介紹了web應(yīng)用各種渲染方案,其中包括客戶端渲染、服務(wù)器端渲染等各種渲染方案。文章翻譯自:https://developers.google.com...。由...
摘要:前端日報精選精讀與提案知乎專欄第期認(rèn)識引擎記錄一次利用工具進(jìn)行性能優(yōu)化的真實(shí)案例簡書中的使用規(guī)則教程繼承的實(shí)現(xiàn)方法個人文章中文譯組件渲染性能探索個人文章周刊第期表單性能的改進(jìn)實(shí)踐知乎專欄簡單可重用的圖表庫知乎專欄 2017-07-08 前端日報 精選 精讀 TC39 與 ECMAScript 提案 - 知乎專欄【第989期】認(rèn)識 V8 引擎記錄一次利用 Timeline/Perform...
摘要:瀏覽器顯示及交互背后的原理引子因?yàn)楣P者愛編程的光頭強(qiáng)近期在寫一本關(guān)于小程序入門的書籍。不基于瀏覽器背后的運(yùn)行原理,是很難說清楚虛擬被引入的真正原因和最大好處的。它是瀏覽器的核心部分。 瀏覽器顯示及交互背后的原理 引子 因?yàn)楣P者(愛編程的光頭強(qiáng))近期在寫一本關(guān)于小程序入門的書籍。其中有一章是介紹虛擬DOM的,它是位于Javascript和真正DOM之間的一層緩存層。為什么引入它,為什么它...
摘要:瀏覽器顯示及交互背后的原理引子因?yàn)楣P者愛編程的光頭強(qiáng)近期在寫一本關(guān)于小程序入門的書籍。不基于瀏覽器背后的運(yùn)行原理,是很難說清楚虛擬被引入的真正原因和最大好處的。它是瀏覽器的核心部分。 瀏覽器顯示及交互背后的原理 引子 因?yàn)楣P者(愛編程的光頭強(qiáng))近期在寫一本關(guān)于小程序入門的書籍。其中有一章是介紹虛擬DOM的,它是位于Javascript和真正DOM之間的一層緩存層。為什么引入它,為什么它...
摘要:在文末,我會附上一個可加載的模型方便學(xué)習(xí)中文藝術(shù)字渲染用原生可以很容易地繪制文字,但是原生提供的文字效果美化功能十分有限。 showImg(https://segmentfault.com/img/bVWYnb?w=900&h=385); WebGL 可以說是 HTML5 技術(shù)生態(tài)鏈中最為令人振奮的標(biāo)準(zhǔn)之一,它把 Web 帶入了 3D 的時代。 初識 WebGL 先通過幾個使用 Web...
閱讀 476·2021-10-09 09:57
閱讀 483·2019-08-29 18:39
閱讀 820·2019-08-29 12:27
閱讀 3036·2019-08-26 11:38
閱讀 2674·2019-08-26 11:37
閱讀 1300·2019-08-26 10:59
閱讀 1387·2019-08-26 10:58
閱讀 996·2019-08-26 10:48