前端效能優化 – 資源預載入

發表於2015-11-19
當提到前端效能優化時,我們首先會聯想到檔案的合併、壓縮,檔案快取和開啟伺服器端的 gzip 壓縮等,這使得頁面載入更快,使用者可以儘快使用我們的 Web 應用來達到他們的目標。 

資源預載入是另一個效能優化技術,我們可以使用該技術來預先告知瀏覽器某些資源可能在將來會被使用到。

引用 Patrick Hamann解釋

預載入是瀏覽器對將來可能被使用資源的一種暗示,一些資源可以在當前頁面使用到,一些可能在將來的某些頁面中被使用。作為開發人員,我們比瀏覽器更加了解我們的應用,所以我們可以對我們的核心資源使用該技術。

這種做法曾經被稱為 prebrowsing,但這並不是一項單一的技術,可以細分為幾個不同的技術:DNS-prefetchsubresource 和標準的 prefetchpreconnectprerender

 

DNS 預解析 DNS-Prefetch

通過 DNS 預解析來告訴瀏覽器未來我們可能從某個特定的 URL 獲取資源,當瀏覽器真正使用到該域中的某個資源時就可以儘快地完成 DNS 解析。例如,我們將來可能從 example.com 獲取圖片或音訊資源,那麼可以在文件頂部的 標籤中加入以下內容:

當我們從該 URL 請求一個資源時,就不再需要等待 DNS 的解析過程。該技術對使用第三方資源特別有用。

在 Harry Roberts 的文章中提到:

通過簡單的一行程式碼就可以告知那些相容的瀏覽器進行 DNS 預解析,這意味著當瀏覽器真正請求該域中的某個資源時,DNS 的解析就已經完成了。

這似乎是一個非常微小的效能優化,顯得也並非那麼重要,但事實並非如此 – Chrome 一直都做了類似的優化。當在瀏覽器的位址列中輸入 URL 的一小段時,Chrome 就自動完成了 DNS 預解析(甚至頁面預渲染),從而為每個請求節省了至關重要的時間。

預連線 Preconnect

與 DNS 預解析類似,preconnect 不僅完成 DNS 預解析,同時還將進行 TCP 握手和建立傳輸層協議。可以這樣使用:

在 Ilya Grigorik 的文章中有更詳細的介紹:

現代瀏覽器都試著預測網站將來需要哪些連線,然後預先建立 socket 連線,從而消除昂貴的 DNS 查詢、TCP 握手和 TLS 往返開銷。然而,瀏覽器還不夠聰明,並不能準確預測每個網站的所有預連結目標。好在,在 Firefox 39 和 Chrome 46 中我們可以使用 preconnect 告訴瀏覽器我們需要進行哪些預連線。

預獲取 Prefetching

如果我們確定某個資源將來一定會被使用到,我們可以讓瀏覽器預先請求該資源並放入瀏覽器快取中。例如,一個圖片和指令碼或任何可以被瀏覽器快取的資源:

與 DNS 預解析不同,預獲取真正請求並下載了資源,並儲存在快取中。但預獲取還依賴於一些條件,某些預獲取可能會被瀏覽器忽略,例如從一個非常緩慢的網路中獲取一個龐大的字型檔案。並且,Firefox 只會在瀏覽器閒置時進行資源預獲取。

在 Bram Stein 的帖子中說到,這對 webfonts 效能提升非常明顯。目前,字型檔案必須等到 DOM 和 CSS 構建完成之後才開始下載,使用預獲取就可以輕鬆繞過該瓶頸。

注意:要測試資源的預獲取有點困難,但在 Chrome 和 Firefox 的網路皮膚中都有資源預獲取的記錄。還需要記住,預獲取的資源沒有同源策略的限制。

Subresources

這是另一個預獲取方式,這種方式指定的預獲取資源具有最高的優先順序,在所有 prefetch 項之前進行:

根據 Chrome 文件

rel=prefetch 為將來的頁面提供了一種低優先順序的資源預載入方式,而 rel=subresource 為當前頁面提供了一種高優先順序的資源預載入。

所以,如果資源是當前頁面必須的,或者資源需要儘快可用,那麼最好使用 subresource 而不是 prefetch

預渲染 Prerender

這是一個核武器,因為 prerender 可以預先載入文件的所有資源:

Steve Souders 在他的一篇文章中寫到:

這類似於在一個隱藏的 tab 頁中開啟了某個連結 – 將下載所有資源、建立 DOM 結構、完成頁面佈局、應用 CSS 樣式和執行 JavaScript 指令碼等。當使用者真正訪問該連結時,隱藏的頁面就切換為可見,使頁面看起來就是瞬間載入完成一樣。Google 搜尋在其即時搜尋頁面中已經應用該技術多年了,微軟也宣稱將在 IE11 中支援該特性。

需要注意的是不要濫用該特性,當你知道使用者一定會點選某個連結時才可以進行預渲染,否則瀏覽器將無條件地下載所有預渲染需要的資源。

更多相關討論:

所有預載入技術都存在一個潛在的風險:對資源預測錯誤,而預載入的開銷(搶佔 CPU 資源,消耗電池,浪費頻寬等)是高昂的,所以必須謹慎行事。雖然很難確定使用者下一步將訪問哪些資源,但高可信的場景確實存在:

  • 如果使用者完成一個帶有明顯結果的搜尋,那麼結果頁面很可能會被載入
  • 如果使用者進入到登陸頁面,那麼登陸成功的頁面很可能會被載入
  • 如果使用者閱讀一個多頁的文章或訪問一個分頁的結果集,那麼下一頁很可能會被載入

最後,使用 Page Visibility API 可以防止頁面真正可見前被執行。

Preload

preload 是一個新規範,與 prefetch 不同(可能被忽略)的是,瀏覽器一定會預載入該資源:

雖然該規範還沒有被所有瀏覽器相容,但其背後的思想還是非常有意思的。

總結

預測使用者下一步將訪問哪些資源是困難的,需要進行大量的測試,但是這帶來的效能提升是明顯的。如果我們願意嘗試這些預獲取技術,一定會顯著提升使用者的體驗。

深入閱讀

原文:Prefetching, preloading, prebrowsing

相關文章