我們知道钟沛,用戶體驗是 Web 產(chǎn)品最為重要的部分啼肩。盡可能減少首屏加載時間忆首,更為流暢地展示用戶所需求的內容,會是用戶是否留存的關鍵因素流码。
而隨著現(xiàn)代 Web 業(yè)務可供用戶的交互行為越來越多,前端項目的復雜度越來越高行施,每個頁面的渲染時間也必然越來越長,這就導致了用戶的體驗不佳魂那,用戶的操作變慢蛾号。
為此,前端工程師們在首屏請求的各個階段中持續(xù)鉆研涯雅,不斷探究如何將首次頁面渲染的時間減少到更小鲜结,力求提供更為優(yōu)秀的產(chǎn)品體驗。
CSR(Client Side Render)
瀏覽器渲染是最簡單活逆,最符合 Web 應用設計思路的渲染方式精刷。
所謂瀏覽器渲染,就是將應用所需的頁面展示蔗候、前端邏輯怒允、接口請求全都在用戶的瀏覽器中執(zhí)行。它很好的實現(xiàn)了前后端的解耦锈遥,讓前端開發(fā)更為獨立纫事,也讓后臺實現(xiàn)更為簡單。
同時所灸,為了緩解用戶的等待焦慮丽惶,我們可以用 loading 態(tài),或者骨架屏爬立,進一步提升異步請求接口時的用戶體驗蚊夫。
不過,隨著業(yè)務復雜程度提高懦尝,瀏覽器渲染的開銷也會變大知纷,我們無法控制用戶側使用的機器性能,很多時候陵霉,用戶使用的機器性能甚至不足以滿足應用的需求琅轧,造成卡頓,甚至崩潰踊挠,這一點在移動端上尤甚乍桂。
而瀏覽器渲染由于前端的動態(tài)性過高,也會帶來 SEO 不佳的問題效床。
SSR(Server Side Render)
服務端渲染的出現(xiàn)時間實際上是要比瀏覽器渲染要更早的睹酌。在 Web 應用發(fā)展的早期,所有的 ASP剩檀、JSP 等模板引擎構建的前端頁面實際上就是服務端渲染的結果憋沿。而此時的服務端渲染無法進行前后端職責的解耦,因此逐步被瀏覽器渲染淘汰沪猴。
但在處理首屏體驗的問題上辐啄,服務端渲染有著獨到的優(yōu)勢采章。它能提前再服務端中完成頁面模板的數(shù)據(jù)填充,從而一次性返回完整的首屏內容壶辜,從而面對 SEO 的爬取時能獲取到更多有效的關鍵信息悯舟。
此外,由于其能快速直出首頁的真實數(shù)據(jù)砸民,體驗往往比 loading 態(tài)更佳抵怎,在 TTI 的表現(xiàn)上更為出色。
但是岭参,服務端渲染也有其自身的局限性反惕。因為從本質上來說,SSR 服務無法完全與前端頁面解耦開來冗荸。因此市面上較完備的 SSR 解決方案都只解決首屏的服務端渲染,并采用同構的方式利耍,增加一層 node 中間層的方式來解決前端與 SSR 服務的更新同步問題蚌本,并與后端開發(fā)項目解耦。
但這無疑增加了項目的復雜度隘梨,并且隨著業(yè)務的復雜程度變高程癌,服務端渲染往往需要調起多個接口去請求數(shù)據(jù)并填充頁面,這樣可能會導致在 TTFB 上有一定劣勢轴猎。
當然嵌莉,最重要的是,服務端渲染對于服務器的負載要求是很高的捻脖。
上圖是引用的字節(jié)的某項目的 SSR 服務的單機 QPS 承載表現(xiàn)锐峭。我們可以看出,對于一個高訪問量的網(wǎng)頁應用來說可婶,提供一個較為復雜的 SSR 服務的成本是相當高的沿癞,需要花費大量的金錢來堆機器。
因此矛渴,從降本增效的角度考慮椎扬,我們需要評估 SSR 帶來的 ROI 是否符合預期。
NSR(Native Side Render)
在移動互聯(lián)網(wǎng)的浪潮下具温,移動端機能飛速提升蚕涤,那么 Web 應用是否能搭上這一班車,將 Native 的性能利用起來铣猩,提升頁面渲染性能呢揖铜?答案是肯定的,這就需要介紹到 NSR 了达皿。
Native 渲染的本質其實還是 SSR蛮位,只不過提供服務的 Server 轉變?yōu)榱丝蛻舳私匣ΑS捎谛枰玫娇蛻舳藱C能,因此此種實現(xiàn)通常應用在移動端 APP失仁,或者 PWA 下尸曼。
當鏈接被點擊時,先借助瀏覽器啟用一個 JS 運行時萄焦,并加載 APP 中存儲的 Html 模板控轿,發(fā)送 xhr 請求預加載頁面數(shù)據(jù),從而在客戶端本地拼接并渲染生成一個有數(shù)據(jù)的 Html 首屏拂封,形成首次 NSR茬射。同時可以將該首屏 Html 緩存在客戶端,供下次頁面打開時冒签,實現(xiàn) stale-while-revalidate
的緩存效果在抛。
由于 NSR 將服務器的渲染工作放在了客戶端的一個個獨立設備中,既實現(xiàn)了頁面的預加載萧恕,同時又不會增加額外的服務器壓力刚梭。達到秒看的效果。
這種能力在擁有客戶端或者支持 PWA 的應用中應用廣泛票唆,例如手 Q朴读,騰訊文檔 APP 中都擁有通過 APP 中的離線包來實現(xiàn)首屏渲染加速的能力。
ESR(Edge Side Render)
那么走趋,對于純 Web 應用衅金,而又由于兼容性等原因暫時無法支持 PWA 的頁面,有沒有一個合適的首屏渲染加速方案呢簿煌?
隨著云與邊緣計算的快速發(fā)展氮唯,前端頁面也需要考慮分布式的請求處理優(yōu)化。
我們知道姨伟,CDN 節(jié)點相比真實服務節(jié)點更貼近用戶您觉,能更快將內容返回。因此我們可以將靜態(tài)的 Html 內容模板緩存在 CDN 上授滓。當接到請求時琳水,先快速將靜態(tài)模板頁面返回給用戶,同時在 CDN 服務器上對頁面動態(tài)部分發(fā)起向后端發(fā)起請求般堆,并將獲取到的動態(tài)內容在以流式下發(fā)的方式繼續(xù)返回給用戶在孝。
這里實際上利用到了 HTTP 的 SSE(Server Send Events)協(xié)議,通過服務器向客戶端發(fā)送單向事件流淮摔,實現(xiàn)同一個 Html 文件的分塊傳輸預渲染私沮。
最佳實踐是?
這也是我們最近在騰訊文檔中探索實踐并落地的和橙,通過服務中間節(jié)點的流式下發(fā)能力仔燕,實現(xiàn)多級首屏渲染加速造垛。
對于一個復雜前端頁面來說,首屏需要加載和運算的資源類型可能有很多晰搀,有需要客戶端解析并執(zhí)行的 JS 動效五辽,也有需要服務端獲取數(shù)據(jù)直出的數(shù)據(jù)分片展示頁面。
通常來說外恕,客戶端只能等待服務端獲取分片數(shù)據(jù)杆逗,并生成經(jīng)過 SSR 渲染后的 HTML,才能開始進行 script 解析與 js 資源拉取的行為鳞疲,最終渲染出完整的頁面數(shù)據(jù)以及動效罪郊。
而既然他們所需要的計算方式不同,那么為什么不能并行來做呢尚洽?
我們可以在版本發(fā)布前悔橄,將未經(jīng)過服務端直出的模板 HTML 進行解析,將需要發(fā)起資源請求的所有的外鏈腳本 url 提取出來腺毫,生成一個 HTML Header 結構癣疟,并將該 Header 內容偽裝為正常 HTML 緩存在 CDN 節(jié)點中。
結合之前我們介紹的 HTTP SSE 協(xié)議拴曲,當用戶請求時争舞,我們可以以最快的速度向用戶返回 CDN 中的 HTML header凛忿,從而讓用戶的瀏覽器提前拉取并解析外鏈資源澈灼。于此同時,CDN 節(jié)點將用戶的請求轉發(fā)給真實的服務端店溢,從而讓服務端進行真實數(shù)據(jù)的獲取拼接并返回給客戶端叁熔。
由于客戶端此時已經(jīng)提前拉取了外鏈資源,因此收到服務端分片的 SSR 后床牧,客戶端可以直接將真實數(shù)據(jù)渲染到頁面中荣回,而不需要再次等待外鏈資源的解析。
由于并行的關系戈咳,這樣的 SSR 與 NSR 混合方式能大大降低復雜頁面首屏渲染的時間心软,提升用戶體驗。
以百度首頁的請求為例著蛙,通過 Chorme Network 提供的瀑布圖删铃,通過我們可以直觀的看到一條請求的執(zhí)行過程。
我們可以看出踏堡,除了 DNS 尋址與 SSL 建連是我們無法控制的以外猎唁,占用請求時間的大頭是 Waiting for server response
,請求服務器 (CDN) 的時間顷蟆,以及 Content Download
诫隅,外鏈資源的拉取時間腐魂。
而使用本文的混合方案后,理論上可以使總請求時間降低到 Max(A, B), (A 為 Waiting for server response
逐纬,B 為 Content Download
) 的水平蛔屹。(當然,實際操作過程中风题,由于 CDN 節(jié)點進行了一次請求轉發(fā)判导,因此擁有 SSR 能力的頁面請求返回時間會更長一些)。
結語
前端的頁面首屏時間優(yōu)化是永恒的話題沛硅,本文介紹了前端界對首屏時間優(yōu)化的進程眼刃,并提供了一種 SSR 與 NSR 混合的新思路,通過并行處理耗時任務的方式摇肌,進一步提升首屏加載時間擂红,希望能夠給大家提供一點參考價值。