- 原文地址:Preload, Prefetch And Priorities in Chrome
- 原文做者:Addy Osmani
- 譯文出自:掘金翻譯計劃
- 譯者:gy134340
- 校對者:IridescentMia,vuuihc
今天咱們來深刻研究一下 Chrome 的網絡協議棧,來更清晰的描述早期網絡加載(像 <link rel=「preload"
和 <link rel=「prefetch」>
)背後的工做原理,讓你對其更加了解。javascript
像其餘文章描述的那樣,preload 是聲明式的 fetch,能夠強制瀏覽器請求資源,同時不阻塞文檔 onload 事件。css
Prefetch 提示瀏覽器這個資源未來可能須要,可是把決定是否和什麼時間加載這個資源的決定權交給瀏覽器。html
Preload 將 load 事件與腳本解析過程解耦,若是你尚未用過它,看看 Yoav Weiss 的文章 Preload: What is it Good For?。前端
在咱們深刻細節以前,下面是上一年發現的使用 proload 並對加載產生積極影響的案例總結:java
Housing.com 在對他們的漸進式 Web 應用程序的腳本轉用 proload 看到大約縮短了10%的可交互時間。react
Shopify 在轉用 preload 加載字體後在 Chrome 桌面版得到了 50%(1.2s) 的文字渲染優化,這徹底解決了他們的文字閃動問題。android
左邊:使用 preload,右邊:不使用 preload(視頻)webpack
使用<link rel=」preload」>
加載字體。ios
Treebo,印度最大的旅館網站之一,在 3G 網絡下對其桌面版試驗,在對其頂部圖片和主要的 Webpack 打包文件使用 preload 以後,在首屏繪製和可交互延遲分別減小了 1s。git
一樣的,在對本身的漸進式 Web 應用程序主要打包文件使用 preload 以後,Flipkart 在路由解析以前 節省了大量的主線程空閒時間(在 3G 網絡下的低性能手機下)。
上面:未使用 preload,下面:使用 preload
Chrome 數據保護團隊在對腳本和 CSS 樣式表使用 preload 以後,發現頁面首次繪製時間得到平均 12% 的速度提高。
對於 prefetch ,它被普遍使用,在 Google 咱們仍用它來獲取能夠加快 搜索結果頁面 的渲染的關鍵資源。
Preload 在不少大型網站都有實際應用,這點你在接下來的文章裏也能夠看到,讓咱們來仔細探討下網絡協議棧其實是如何對待 preload 和 prefetch 的。
<link rel=」preload」>
? 何時又該用 <link rel=」prefetch」>
?建議:對於當前頁面頗有必要的資源使用 preload,對於可能在未來的頁面中使用的資源使用 prefetch。
preload 是對瀏覽器指示預先請求當前頁須要的資源(關鍵的腳本,字體,主要圖片)。
prefetch 應用場景稍微又些不一樣 —— 用戶未來可能在其餘部分(好比視圖或頁面)使用到的資源。若是 A 頁面發起一個 B 頁面的 prefetch 請求,這個資源獲取過程和導航請求多是同步進行的,而若是咱們用 preload 的話,頁面 A 離開時它會當即中止。
使用 preload 和 prefetch,咱們有了對當前頁面和未來頁面加載關鍵資源的解決辦法。
<link rel="preload">
和 <link rel="prefetch">
的緩存行爲Chrome 有四種緩存: HTTP 緩存,內存緩存,Service Worker 緩存和 Push 緩存。preload 和 prefetch 都被存儲在 HTTP 緩存中。
當一個資源被 preload 或者 prefetch 獲取後,它能夠從 HTTP 緩存移動至渲染器的內存緩存中。若是資源能夠被緩存(好比說存在有效的cache-control 和 max-age),它被存儲在 HTTP 緩存中能夠被如今或未來的任務使用,若是資源不能被緩存在 HTTP 緩存中,做爲代替,它被放在內存緩存中直到被使用。
下面是在 Blink 內核的 Chrome 46 及更高版本中不一樣資源的加載優先級狀況( Pat Meenan)
preload 用 「as」 或者用 「type」 屬性來表示他們請求資源的優先級(好比說 preload 使用 as="style" 屬性將得到最高的優先級)。沒有 「as」 屬性的將被看做異步請求,「Early」意味着在全部未被預加載的圖片請求以前被請求(「late」意味着以後),感謝 Paul Irish 更新這張關於開發者工具以及網絡層上各類請求優先級的表。
咱們來談一下這張表。
腳本根據它們在文件中的位置是否異步、延遲或阻塞得到不一樣的優先級:
圖片(視口可見)將會得到相對於視口不可見圖片(低級)的更高的優先級(中級),因此某些程度上 Chrome 將會盡可能懶加載這些圖片。低優先級的圖片在佈局完成被視口發現時,將會得到優先級提高(可是注意已經在佈局完成後的圖片將不會更改優先級)。
preload 使用 「as」 屬性加載的資源將會得到與資源 「type」 屬性所擁有的相同的優先級。好比說,preload as="style" 將會得到比 as=「script」 更高的優先級。這些資源一樣會受內容安全策略的影響(好比說,腳本會受到其 「src」 屬性的影響)。
不帶 「as」 屬性的 preload 的優先級將會等同於異步請求。
若是你想了解各類資源加載時的優先級屬性,從開發者工具的 Timeline/Performance 區域的 Network 區域都能看到相關信息:
在 Network 面板下的「Priority」部分
這就要說看狀況了,但一般來講,會是比較好的狀況 —— 若是資源沒有超出 HTTP 緩存時間或者 Service Worker 沒有主動從新發起請求,那麼瀏覽器就不會再去請求這個資源了。
若是資源在 HTTP 緩存( Service Worker 緩存和網絡中),那麼 preload 將會得到一次緩存命中。
**用「preload」和「prefetch」狀況下,若是資源不能被緩存,那麼都有可能浪費一部分帶寬。
沒有用到的 preload 資源在 Chrome 的 console 裏會在 onload 事件 3s 後發生警告。
緣由是你可能爲了改善性能使用 preload 來緩存必定的資源,可是若是沒有用到,你就作了無用功。在手機上,這至關於浪費了用戶的流量,因此明確你要 preload 對象。
preload 和 prefetch 是很簡單的工具,你很容易不當心二次獲取。
不要用 「prefetch」 做爲 「preload」 的後備,它們適用於不一樣的場景,經常會致使不符合預期的二次獲取。使用 preload 來獲取當前須要任務不然使用 prefetch 來獲取未來的任務,不要一塊兒用。
不要期望 preload 和 fetch() 配合使用,在 Chrome 中這樣使用將會致使二次的下載。這並不僅發生在異步請求的狀況咱們有一個關於這個問題公開的 bug。
對 preload 使用 「as」 屬性,否則將不會從中獲益。
若是你對你所 preload 的資源使用明確的 「as」 屬性,好比說,腳本,你將會致使二次獲取。
preload 字體不帶 crossorigin 也將會二次獲取! 確保你對 preload 的字體添加 crossorigin 屬性,不然他會被下載兩次,這個請求使用匿名的跨域模式。這個建議也適用於字體文件在相同域名下,也適用於其餘域名的獲取(好比說默認的異步獲取)。
存在 intergrity 屬性的資源不能使用 preload 屬性(目前)也會致使二次獲取。連接元素的 [integrity](https://bugs.chromium.org/p/chromium/issues/detail?id=677022)
屬性目前尚未被支持,目前有一個關於它的開放issue,這意味着存在 integrity 的元素將會丟棄 preload 的資源。往寬了講,這會致使重複的請求,你須要在安全和性能之間做出權衡。
最後,雖然它不會致使二次獲取,仍是有下面的建議:
不要 preload 全部東西! 做爲替代的,用 preload 來告訴瀏覽器一些原本不能被提前發現的資源,以便提前獲取它們。
這是工具而不是規則的好例子。你 preload 的文件數量取決於加載其餘資源時網絡內容、用戶的帶寬和其餘網絡情況。
儘早 preload 頁面中可能須要的文件,對於腳本文件,preload 關鍵打包文件頗有用由於它將加載與執行分離開來,script async
很差由於它會阻塞 window 的 onload 事件。你能夠儘早加載圖片、樣式、字體和媒體資源。大部分的 —— 最重要的是,你做爲做者是能夠清晰的知道哪些東西是頁面目前須要的。
在 Chrome 中,若是用戶從一個頁面跳轉到另外一個頁面,prefetch 發起的請求仍會進行不會中斷。
另外,prefetch 的資源在網絡堆棧中至少緩存 5 分鐘,不管它是否是能夠緩存的。
preload 將資源獲取與執行解耦,像這樣,preload 在標記中聲明以被 Chrome preload 掃描器掃描。這意味着,在許多案例中,在 HTML 解析器獲取到標籤以前,preload 就會被獲取(用它聲明的優先級)。這將會比自定義的 preload 更增強大。
當你知道資源加載的正確順序時使用推送,用 service worker 來攔截那些可能須要會致使二次獲取的資源請求,用 preload 來加快第一個請求的開始時間 —— 這對全部的資源獲取都有用。
再次說一下,這都要看狀況,咱們試想一下位 Google Play 商店作購物車,對於一個向購物車的請求:
用 preload 來加載頁面的主要的模塊須要瀏覽器等待 play.google.com/cart 有效載荷以便 preload 掃描器發現依賴,但這以後會浸透網絡管道能夠更好的像資源發起請求,這可能不是最理想的冷啓動,但對於高速緩存和帶寬的後續請求很是友好。
使用 HTTP/2 的服務器推送,當請求 play.google.com/cart 咱們能夠快速浸透網絡管道,但若是資源已經在 HTTP 或者 Service Worker 緩存中的話咱們就浪費了帶寬,兩種方法都須要作出權衡。
雖然推送頗有效,但它不像 preload 那樣對全部的狀況都適應。
preload 利於下載與執行的解耦,多虧其對文檔 onload 事件的支持咱們如今能夠控制其加載完畢後的事件,獲取 JS 包文件在空閒快執行或者獲取 CSS 模塊在正確的時間點執行,能夠說是很是強大的。
推送不能用於第三方資源的內容,經過當即發送資源,它還有效地縮短瀏覽器自身的資源優先級狀況。在你明確的知道在作什麼時,這應該會提升你的應用性能,若是不是很清晰的話,你也許會損失掉部分的性能。
跟其餘連接不一樣,preload 連接便可以放在 HTML 標籤裏也能夠放在 HTTP 頭部(preload HTTP 頭),每種狀況下,都會直接使瀏覽器加載資源並緩存在內存裏,代表頁面有很高的可能性用這些資源而且不想等待 preload 掃描器或者解析器去發現它。
當金融時報在它們的網站使用 preload HTTP 頭時,他們節約了大約 1s 的顯示片頭圖片時間。
下面的:使用 preload,上面:使用 preload。在 3G 網絡下的 Moto G4 測試。
原來:www.webpagetest.org/result/1703…,以後: www.webpagetest.org/result/1703…。你可使用兩種形式的 preload,但應當知道很重要的一點:根據規範,許多服務器當它們遇到 preload HTTP 頭會發起 HTTP/2 推送,HTTP/2 推送的性能影響不一樣於普通的預加載,因此你要確保沒有發起沒必要要的推送。
你可使用 preload 標籤來代替 preload 頭以免沒必要要的推送,或者在你的 HTTP 頭上加一個 「nopush」 屬性。
用下面的代碼段能夠檢測<link rel=」preload」>
是否被支持:
const preloadSupported = () => {
const link = document.createElement('link');
const relList = link.relList;
if (!relList || !relList.supports)
return false;
return relList.supports('preload');
};複製代碼
FilamentGroup 也有一個 preload 檢測器 ,做爲他們的異步 CSS 加載庫 loadCSS 的一部分。
固然,preload 支持基於異步加載的標記,使用 <link rel=」preload」>
的樣式表使用 onload
事件當即應用到文檔:
<link rel="preload" href="style.css" onload="this.rel=stylesheet">複製代碼
更多相關的例子,看一下 Yoav Weiss 很棒的使用實例。
根據 HTTPArchive,不少網站應用 <link rel=」preload」>
來加載字體,包括 Teen Vogue 和以上提到的其餘網站:
其餘一些網站,好比 LifeHacker 和 JCPenny 用 FilamentGroup 的 loadCSS 來異步加載 CSS:
有愈來愈多的漸進式 Web 應用程序(好比 Twitter.com 移動端, Flipkart 和 Housing)使用它來加載當前連接須要的腳本:
基本的觀點是要保持高粒度而不是單片,因此任何應用均可以按需加載依賴或者預加載資源並放在緩存中。
根據 CanIUse 在 Safari Tech Preview的調查看,<link rel="preload">
大約有 50% 的支持度,<link rel="prefetch">
大約有 70% 的支持度。<link rel="preload">
is available to ~50% of the global population according to CanIUse and is implemented in the Safari Tech Preview. <link rel="prefetch">
is available to 71% of global users.
感謝 @ShopifyEng、@AdityaPunjani、@HousingEngg、@adgad、@wheresrhys 和 @__lakshya 分享的統計信息。
很是感謝下列技術審覈與建議人員: Ilya Grigorik, Gray Norton, Yoav Weiss, Pat Meenan, Kenji Baheux, Surma, Sam Saccone, Charles Harrison, Paul Irish, Matt Gaunt, Dru Knox, Scott Jehl.
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、React、前端、後端、產品、設計 等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃。