前端性能優化 - 資源預加載

jopen 9年前發布 | 28K 次閱讀 前端 前端技術

當提到前端性能優化時,我們首先會聯想到文件的合并、壓縮,文件緩存和開啟服務器端的 gzip 壓縮等,這使得頁面加載更快,用戶可以盡快使用我們的 Web 應用來達到他們的目標。

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

引用 Patrick Hamann解釋

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

</blockquote>

這種做法曾經被稱為 prebrowsing,但這并不是一項單一的技術,可以細分為幾個不同的技術:DNS-prefetch、subresource和標準的prefetch、preconnect、prerender。

DNS 預解析 DNS-Prefetch

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

</tr> </tbody> </table>

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

在 Harry Roberts 的文章中提到:

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

</blockquote>

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

預連接 Preconnect

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

1 
<link rel="dns-prefetch" href="http://example.com"> 

</tr> </tbody> </table>

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

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

</blockquote>

預獲取 Prefetching

如果我們確定某個資源將來一定會被使用到,我們可以讓瀏覽器預先請求該資源并放入瀏覽器緩存中。例如,一個圖片和腳本或任何可以被瀏覽器緩存的資源:

1 
<link rel="preconnect" href="http://example.com"> 

</tr> </tbody> </table>

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

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

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

Subresources

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

1 
<link rel="prefetch" href="image.png"> 

</tr> </tbody> </table>

根據 Chrome 文檔

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

</blockquote>

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

預渲染 Prerender

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

1 
<link rel="subresource" href="styles.css"> 

</tr> </tbody> </table>

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

這類似于在一個隱藏的 tab 頁中打開了某個鏈接 – 將下載所有資源、創建 DOM 結構、完成頁面布局、應用 CSS 樣式和執行 JavaScript 腳本等。當用戶真正訪問該鏈接時,隱藏的頁面就切換為可見,使頁面看起來就是瞬間加載完成一樣。Google 搜索在其即時搜索頁面中已經應用該技術多年了,微軟也宣稱將在 IE11 中支持該特性。

</blockquote>

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

更多相關討論:

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

  • 如果用戶完成一個帶有明顯結果的搜索,那么結果頁面很可能會被加載
  • 如果用戶進入到登陸頁面,那么登陸成功的頁面很可能會被加載
  • 如果用戶閱讀一個多頁的文章或訪問一個分頁的結果集,那么下一頁很可能會被加載
  • </ul> </blockquote>

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

    Preload

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

1 
<link rel="prerender" href="http://example.com"> 

</tr> </tbody> </table>

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

總結

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

深入閱讀

1 
<link rel="preload" href="image.png">? 
  • sesese色