簡單來說,今天要說的是性能問題。大家上網時都想馬上看到自己想看的內容,而且我們也發現快速的啟動能提高用戶滿意度。因此,當我們著手做期待已久的Neflix.com升級任務時,網站的界面工程團隊把啟動性能作為最高優先級的任務。
這次我們把啟動時間縮短了70%,主要在以下三個方面做了改善:
1. 服務器和客戶端渲染
2. 通用JavaScript
3. 降低JavaScript負載
服務器和客戶端渲染
舊版netflix.com網站堆棧在服務器標記語言和客戶端轉化之間有著硬性的分離。這主要是由于我們應用的各個部分使用了不同的編程語言。在服務器端,我們使用的是配合Tomcat,Struts和Tiles使用的Java語言。在瀏覽器端,我們通過JavaScript來轉化服務器生成的標記語言,主要使用jQuery。
這樣的分離導致啟動時間很不理想。每當用戶訪問netflix.com上的頁面,我們的Java層響應的時候都要處理整個頁面并轉成HTML發送。于是用戶需要一直等待直到整個頁面完全生成,而其中大部分內容他們并不感興趣。
我們的新架構只渲染頁面的一小部分,自動生成客戶端視圖。我們可以改變服務器生成的總視圖的數量,這樣便于觀察因此產生的正面或負面的影響。服務器響應時需要的數據很少,將數據轉換為DOM元素時花的時間也更少。客戶端JavaScript接管后,它會檢查當前視圖的數據以及后續會話要求的視圖的數據。這樣做最大好處是減少了服務器處理時間,并且把渲染整合到同一種語言中。
我們發現服務器和客戶端分別渲染還提供了一種靈活性,允許我們選擇哪些部分在服務器端渲染,哪些在客戶端渲染,于是實現了更快的啟動和視圖之間的平滑切換。
通用的JavaScript
為了在服務器端和客戶端支持相同的渲染,我們要重新考慮我們的渲染路徑。必須放棄我們以前的架構中把在服務器端生成標記語言與在客戶端進行強化這二者分離的做法。
三大痛點促使我們使用新的Node.js架構:
1. 語言之間的內容切換不理想。
2. 強化標記語言需要太多用于生成標記語言的服務器專用代碼和用于強化的客戶端專用代碼的直接耦合。
3. 我們更傾向于使用同一種API生成所有的標記語言。
針對這個問題,實際上有許多不需要用到Universal JavaScript的解決方法,但是基于如下原因我們發現這個方案是最合適的:如果同一個東西有兩個副本,那這兩個副本之間很容易產生微小的差異。使用Universal JavaScript意味著只需要把渲染邏輯發送到客戶端。
Node.js和React.js天然適用于這類應用場景。通過Node.js和React.js,我們可以在服務器進行渲染,然后在最初的標記語言和React.js的內容發送到瀏覽器之后,再在客戶端對整體的變化進行渲染。這種靈活性允許應用程序在不同的位置獨立地進行相同的渲染輸出。服務器端和客戶端的硬性分離不復存在,它們的渲染輸出也就不會有所不同。
降低JavaScript負載的影響
在網站上實現豐富的交互體驗,對用戶來說往往會轉變為沉重的JavaScript負載。在我們的新架構中,我們特別注意把一些大的依賴庫替換成多個小模塊,并且只對當前訪問的用戶發送適合他們的JavaScript。
很多在舊架構中用過的大的依賴庫沒有繼續用在新架構中,我們把它們替換成一批新的、更有效率的庫。更新這些庫使得JavaScript的負載變小了很多,這意味著人們在訪問的時候只需要很少的JavaScript。我們知道這個部分仍然有很多要完善的地方,我們正在努力使得JavaScript負載進一步下降。
交互時間
為了了解這些改進帶來的影響,我們監控了一個叫交互時間(tti)的參數。
交互時間指的是應用平臺首次啟動與UI界面互動響應之間的時間量。注意,這并不需要UI界面完全加載,而是用戶可以使用輸入設備與UI進行交互的第一時間點。
對于在瀏覽器中運行的應用,這個數據可以從Navigation Timing API得到。
工作仍在繼續
我們堅信,高性能并不是一個隨意的工程目標——它是創造良好用戶體驗的硬性要求。我們已經在啟動性能方面取得了顯著進步,并將在致力于為用戶持續提供更好的用戶體驗的過程中,不斷嘗試挑戰行業的最佳做法。
接下來的幾個月,我們會研究Service Workers, ASM.js, Web Assembly和其它新興的網絡標準,看看能否利用它們實現更高性能的網站體驗。