[譯] Netflix 的 Web 效能案例研究

提綱:Web 效能優化沒有銀彈。簡單的靜態網頁得益於使用極少 JavaScript 程式碼的服務端渲染。庫的謹慎使用可以為複雜的頁面帶來巨大的價值。
Netflix 是最受歡迎的視訊流服務之一。自 2016 年在全球推出以來,公司發現許多新使用者不僅通過移動裝置完成註冊,而且還使用了不太理想的網路連線。
通過改進用於Netflix.com 註冊過程 JavaScript 程式碼和使用預載入技術,開發人員團隊可以為移動和桌面使用者提供更好的使用者體驗,並提供多項改進。
- 減少 50% 的載入和可互動時間(適用於Netflix.com 桌面端未登入的主頁)
- 通過把 React 和其他客戶端庫改為原生的 JavaScript 使打包大小減少 200 KB。React 仍在服務端使用
- 為將來的操作預獲取 HTML,CSS 和 JavaScript(React)使可互動時間減少 30%
通過嵌入更少的程式碼來減少可互動時間
Netflix 開發者優化效能的地方是未登入主頁,使用者在此頁面註冊並登入站點。

新使用者和已登出使用者的Netflix.com 主頁
此頁面初始包含 300KB 的 JavaScript 程式碼,其中一些是 React 和其他客戶端程式碼(例如像 Lodash 的工具庫),而且還有一些是必要的上下文資料用來給 React 的狀態注水(hydrate)。
所有 Netflix 的網頁都由服務端 React 渲染,這些頁面為生成的 HTML 和客戶端應用提供服務,因此維持新優化的主頁結構不變和保持開發人員體驗的一致性同樣重要。

Homepage 選項卡是最初使用 React 編寫的元件的示例
使用 Chrome 的 DevTools 和 Lighthouse 來模擬 3G 網路下載入未登入主頁,結果顯示未登入主頁需要 7 秒時間來載入,這段時間對於一個簡單的入口頁面來說實在是太久了,所以我們開始調查改進的可能性。通過一些效能審查,Netflix 發現他們的客戶端 JS 有過高的開銷。

通過 Chrome DevTools 的網路限速功能,檢視未優化的Netflix.com 的表現。
通過關閉瀏覽器中的 JavaScript 來觀察站點中仍在起作用的元素,開發者團隊可以決定 React 在未登入主頁是否真正必要。
由於頁面中的多數元素是基本的 HTML,剩下的元素比如 JavaScript 點選處理和新增類可以用原生 JavaScript 來替換,而頁面原來使用 React 實現的語言切換器則使用不到 300 行的原生 JavaScript 程式碼重構。
移植到原生 JavaScript 的元件完全列表:
- 基礎互動(主頁中的選項卡)
- 語言切換器
- Cookie 橫幅(針對非美國訪問者)
- 分析用的客戶端日誌
- 效能評估和記錄
- 廣告來源引導程式碼(出於安全考慮,沙盒化放在 iframe 裡)

雖然 React 的初始程式碼僅僅 45 KB,在客戶端移除 React、一些庫和相應的 App 程式碼 減少的 JavaScript 程式碼總量超多 200 KB ,由此在 Netflix 的未登入主頁降低了超過 50% 的可互動時間。

移除客戶端 React、Lodash 和其他一些庫前後的負載比較。
在實驗環境下,我們可以使用Lighthouse(trace)快速測驗使用者是否能與 Netflix 主頁互動。結果桌面端的 TTI 少於 3.5s。

可互動時間優化後的 Lighthouse 報告。
那麼這個領域的度量標準呢?使用Chrome 使用者體驗報告我們可以看到首次輸入延遲 —— 從使用者首次與你的站點互動時間到瀏覽器真正響應那次互動的時間 —— 對於 97% 的 Netflix 桌面使用者來說很快。結果非常棒。

首先輸入延遲(FID)度量使用者在與頁面互動時的延遲體驗。
為後續頁面預載入 React
為了進一步提高瀏覽登入主頁的效能,Netflix 利用使用者在入口頁面上花費的時間針對可能會登入的下一個頁面進行資源 預載入 。
通過兩項技術完成 —— 內建的 HTTP%2FLink_prefetching_FAQ" rel="nofollow,noindex"> <link rel=prefetch>
瀏覽器 API 和 XHR 預載入。
內建的瀏覽器 API 包含頁面頭部標籤內的簡單鏈接標籤。它會建議瀏覽器資源(例如 HTML、JS、CSS、圖片)可以被預載入,雖然它並不保證瀏覽器真的 會 預載入資源,並且它缺少其他瀏覽器的全面支援。

預載入技術對比
另一方面,XHR 預載入已經成為瀏覽器標準很多年了,當 Netflix 團隊提示瀏覽器快取資源時,其成功率達到 95%。但是 XHR 預載入不能預載入 HTML 文件,Netflix 用它來為後續頁面預載入 JavaScript 和 CSS 打包檔案。
注意:Netflix 配置的 HTTP 響應頭禁止使用 XHR 快取 HTML(它們確實不快取(no-cache)第二個頁面的 HTML)。連結預載入會按預期工作,因為它對 HTML 有效,即使設定了不快取(no-cache)。
// 建立新的 XHR 請求 const xhrRequest = new XMLHttpRequest(); // open the request for the resource to "prefetch" // 開啟請求來“預載入”資源 xhrRequest.open('GET', '../bundle.js', true); // 傳送! xhrRequest.send(); 複製程式碼
通過使用瀏覽器內建 API 和 XHR 預載入 HTML、CSS 和 JS,可互動時間減少了 30%。這個實現不需要重寫 JavaScript,也不會對未登入主頁的效能造成負面影響,而且從此以後,能以極低的風險為提升頁面效能提供了非常有價值的工具。

預載入實現之後,Netflix 開發者可以通過分析頁面減少的可互動時間資料來觀察效能提升效果,同樣使用 Chrome 開發工具直接度量資源快取的命中情況。
Netflix 未登入主頁 —— 優化總結
通過預載入 Netflix 未登入主頁資源和優化客戶端程式碼,Netflix 可以在註冊過程中出色地提升可互動時間指標。通過使用瀏覽器內建 API 和 XHR 預載入來預獲取未來頁面,Netflix 可以把可互動時間降低 30%。這是針對下一頁面的載入,其中包含單頁應用註冊過程的引導程式碼。
Netflix 團隊進行的程式碼優化表明,React 是一個十分有用的庫,不過它可能無法為每個問題提供足夠的解決方案。通過從第一個用於註冊的入口頁面的客戶端程式碼中刪除 React,可互動時間減少了 50% 以上。縮短客戶端上的可互動時間還可以讓使用者以更快地速度單擊註冊按鈕,這表明程式碼優化完全可以帶來更好的使用者體驗。
雖然 Netflix 沒有在主頁中使用 React,但他們為後續的頁面預載入。這使得他們整個頁面應用程式流程中的其他部分可以利用客戶端 React。
更多關於這些優化的細節,請觀看 Tony Edwards 的出色演講:
總結
通過密切關注 JavaScript 的開銷,Netflix 發現了改善可互動時間的機會。若想發現你的站點是否有機會在這點上做得更好,可以藉助你的效能工具。
Netflix 決定做出的權衡是使用 React 對入口頁面進行伺服器渲染,同時也在其上預先獲取 React 和其餘註冊流程的程式碼。這樣可以優化首次載入效能,同時還可以優化其餘註冊流的載入時間,因為它是一個單頁應用程式,因此需要下載更大的 JS 打包檔案。
考慮一下是否使用原生 JavaScript 是否適合你的站點的流程。如果你確實需要使用庫,那麼嘗試只嵌入你的使用者需要的程式碼。預載入技術可以幫助優化未來瀏覽頁面的載入時間。