我們知道,使用者體驗是 Web 產品最為重要的部分。儘可能減少首屏載入時間,更為流暢地展示使用者所需求的內容,會是使用者是否留存的關鍵因素。
而隨著現代 Web 業務可供使用者的互動行為越來越多,前端專案的複雜度越來越高,每個頁面的渲染時間也必然越來越長,這就導致了使用者的體驗不佳,使用者的操作變慢。
為此,前端工程師們在首屏請求的各個階段中持續鑽研,不斷探究如何將首次頁面渲染的時間減少到更小,力求提供更為優秀的產品體驗。
瀏覽器渲染是最簡單,最符合 Web 應用設計思路的渲染方式。
所謂瀏覽器渲染,就是將應用所需的頁面展示、前端邏輯、介面請求全都在使用者的瀏覽器中執行。它很好的實現了前後端的解耦,讓前端開發更為獨立,也讓後臺實現更為簡單。
同時,為了緩解使用者的等待焦慮,我們可以用 loading 態,或者骨架屏,進一步提升非同步請求介面時的使用者體驗。
不過,隨著業務複雜程度提高,瀏覽器渲染的開銷也會變大,我們無法控制使用者側使用的機器效能,很多時候,使用者使用的機器效能甚至不足以滿足應用的需求,造成卡頓,甚至崩潰,這一點在行動端上尤甚。
而瀏覽器渲染由於前端的動態性過高,也會帶來 SEO 不佳的問題。
伺服器端渲染的出現時間實際上是要比瀏覽器渲染要更早的。在 Web 應用發展的早期,所有的 ASP、JSP 等模板引擎構建的前端頁面實際上就是伺服器端渲染的結果。而此時的伺服器端渲染無法進行前後端職責的解耦,因此逐步被瀏覽器渲染淘汰。
但在處理首屏體驗的問題上,伺服器端渲染有著獨到的優勢。它能提前再伺服器端中完成頁面模板的資料填充,從而一次性返回完整的首屏內容,從而面對 SEO 的爬取時能獲取到更多有效的關鍵資訊。
此外,由於其能快速直出首頁的真實資料,體驗往往比 loading 態更佳,在 TTI 的表現上更為出色。
但是,伺服器端渲染也有其自身的侷限性。因為從本質上來說,SSR 服務無法完全與前端頁面解耦開來。因此市面上較完備的 SSR 解決方案都只解決首屏的伺服器端渲染,並採用同構的方式,增加一層 node 中間層的方式來解決前端與 SSR 服務的更新同步問題,並與後端開發專案解耦。
但這無疑增加了專案的複雜度,並且隨著業務的複雜程度變高,伺服器端渲染往往需要調起多個介面去請求資料並填充頁面,這樣可能會導致在 TTFB 上有一定劣勢。
當然,最重要的是,伺服器端渲染對於伺服器的負載要求是很高的。
上圖是參照的位元組的某專案的 SSR 服務的單機 QPS 承載表現。我們可以看出,對於一個高存取量的網頁應用來說,提供一個較為複雜的 SSR 服務的成本是相當高的,需要花費大量的金錢來堆機器。
因此,從降本增效的角度考慮,我們需要評估 SSR 帶來的 ROI 是否符合預期。
在行動網際網路的浪潮下,行動端機能飛速提升,那麼 Web 應用是否能搭上這一班車,將 Native 的效能利用起來,提升頁面渲染效能呢?答案是肯定的,這就需要介紹到 NSR 了。
Native 渲染的本質其實還是 SSR,只不過提供服務的 Server 轉變為了使用者端。由於需要用到使用者端機能,因此此種實現通常應用在行動端 APP,或者 PWA 下。
當連結被點選時,先借助瀏覽器啟用一個 JS 執行時,並載入 APP 中儲存的 Html 模板,傳送 xhr 請求預載入頁面資料,從而在使用者端本地拼接並渲染生成一個有資料的 Html 首屏,形成首次 NSR。同時可以將該首屏 Html 快取在使用者端,供下次頁面開啟時,實現 stale-while-revalidate
的快取效果。
由於 NSR 將伺服器的渲染工作放在了使用者端的一個個獨立裝置中,既實現了頁面的預載入,同時又不會增加額外的伺服器壓力。達到秒看的效果。
這種能力在擁有使用者端或者支援 PWA 的應用中應用廣泛,例如手 Q,騰訊檔案 APP 中都擁有通過 APP 中的離線包來實現首屏渲染加速的能力。
那麼,對於純 Web 應用,而又由於相容性等原因暫時無法支援 PWA 的頁面,有沒有一個合適的首屏渲染加速方案呢?
隨著雲與邊緣計算的快速發展,前端頁面也需要考慮分散式的請求處理優化。
我們知道,CDN 節點相比真實服務節點更貼近使用者,能更快將內容返回。因此我們可以將靜態的 Html 內容模板快取在 CDN 上。當接到請求時,先快速將靜態模板頁面返回給使用者,同時在 CDN 伺服器上對頁面動態部分發起向後端發起請求,並將獲取到的動態內容在以流式下發的方式繼續返回給使用者。
這裡實際上利用到了 HTTP 的 SSE(Server Send Events)協定,通過伺服器向用戶端傳送單向事件流,實現同一個 Html 檔案的分塊傳輸預渲染。
這也是我們最近在騰訊檔案中探索實踐並落地的,通過服務中間節點的流式下發能力,實現多級首屏渲染加速。
對於一個複雜前端頁面來說,首屏需要載入和運算的資源型別可能有很多,有需要使用者端解析並執行的 JS 動效,也有需要伺服器端獲取資料直出的資料分片展示頁面。
通常來說,使用者端只能等待伺服器端獲取分片資料,並生成經過 SSR 渲染後的 HTML,才能開始進行 script 解析與 js 資源拉取的行為,最終渲染出完整的頁面資料以及動效。
而既然他們所需要的計算方式不同,那麼為什麼不能並行來做呢?
我們可以在版本釋出前,將未經過伺服器端直出的模板 HTML 進行解析,將需要發起資源請求的所有的外連指令碼 url 提取出來,生成一個 HTML Header 結構,並將該 Header 內容偽裝為正常 HTML 快取在 CDN 節點中。
結合之前我們介紹的 HTTP SSE 協定,當用戶請求時,我們可以以最快的速度向用戶返回 CDN 中的 HTML header,從而讓使用者的瀏覽器提前拉取並解析外連資源。於此同時,CDN 節點將使用者的請求轉發給真實的伺服器端,從而讓伺服器端進行真實資料的獲取拼接並返回給使用者端。
由於使用者端此時已經提前拉取了外連資源,因此收到伺服器端分片的 SSR 後,使用者端可以直接將真實資料渲染到頁面中,而不需要再次等待外連資源的解析。
由於並行的關係,這樣的 SSR 與 NSR 混合方式能大大降低複雜頁面首屏渲染的時間,提升使用者體驗。
以百度首頁的請求為例,通過 Chorme Network 提供的瀑布圖,通過我們可以直觀的看到一條請求的執行過程。
我們可以看出,除了 DNS 定址與 SSL 建連是我們無法控制的以外,佔用請求時間的大頭是 Waiting for server response
,請求伺服器 (CDN) 的時間,以及 Content Download
,外連資源的拉取時間。
而使用本文的混合方案後,理論上可以使總請求時間降低到 Max(A, B), (A 為 Waiting for server response
,B 為 Content Download
) 的水平。(當然,實際操作過程中,由於 CDN 節點進行了一次請求轉發,因此擁有 SSR 能力的頁面請求返回時間會更長一些)。
前端的頁面首屏時間優化是永恆的話題,本文介紹了前端界對首屏時間優化的程序,並提供了一種 SSR 與 NSR 混合的新思路,通過並行處理耗時任務的方式,進一步提升首屏載入時間,希望能夠給大家提供一點參考價值。