摘要: 理解Preload與Prefetch。javascript
Fundebug經受權轉載,版權歸原做者全部。css
這是 Web 性能優化的第 6 篇,上一篇在下面看點擊查看:html
- Web 性能優化:使用 Webpack 分離數據的正確方法
- Web 性能優化:圖片優化讓網站大小減小 62%
- Web 性能優化:緩存 React 事件來提升性能
- Web 性能優化:21種優化CSS和加快網站速度的方法
- Web 性能優化:理解及使用 JavaScript 緩存
今天,咱們將深刻研究Chrome 的網絡棧,以明確 web 加載原語(如<link rel= preload >
& <link rel= prefetch >
) 背後的工做原理,以便你可以更有效地使用它們。前端
如其餘文章所述,preload 是一個聲明式 fetch
,能夠強制瀏覽器在不阻塞 document
的 onload 事件的狀況下請求資源。java
Prefetch
告訴瀏覽器這個資源未來可能須要,可是什麼時間加載這個資源是由瀏覽器來決定的。webpack
在預加載(perload)以前,網絡請求從這裏開始,預加載以後,它在解析時從左向右移動git
使用預加載(perload)的一些案例
在詳細介紹 預加載(perload) 以前,先來看看一些使用 預加載(perload) 的案例。github
Housing.com 在對他們的漸進式 Web 應用程序的腳本轉用 proload 看到大約縮短了10%的可交互時間。web
Shopify 使用 [preload 加載 Web字體](https://www.bramstein.com/writing/preload-hints-for-web-fo nts.html)後,Chrome 桌面版)的文本繪製時間(1.2秒)提升了50%,這徹底解決了他們的文字閃動問題。chrome
左邊:使用 preload,右邊:不使用 preload
使用<link rel="preload"> 加載字體
Treebo,印度最大的旅館網站之一,在 3G 網絡下對其桌面版試驗,在對其頂部圖片和主要的 Webpack 打包文件使用 preload
以後,在首屏繪製和可交互延遲分別減小了 1s。
一樣的,在對本身的漸進式 Web 應用程序主要打包文件使用 preload 以後,Flipkart 在路由解析以前 節省了大量的主線程空閒時間(在 3G 網絡下的低性能手機下)。
上面:沒有使用 proload 加載,下面:使用 preload 加載
Chrome 數據保護程序團隊發現,對於那些能夠在腳本和 CSS 樣式表上使用 preload
的頁面,發現頁面首次繪製時間得到平均 12% 的速度提高。
對於 prefetch(預讀取)
,它被普遍使用,在 Google 咱們仍用它來預讀取
一些能夠加快 搜索結果頁面 的渲染的關鍵資源。
Preload
在大型網站中都有很好運用,你能夠在本文後面找到更多這些安全。 在此以前,讓咱們深刻了解網絡堆棧如何實際處理 預加載(prefetch)與預讀取(prefetch)。
什麼時候使用 <link rel="preload"> 和 <link rel="prefetch"> ?
提示:
preload
加載資源通常是當前頁面須要的,prefetch
通常是其它頁面有可能用到的資源。
preload
是告訴瀏覽器預先請求當前頁面須要的資源(關鍵的腳本,字體,主要圖片等)。
prefetch
應用場景稍微又些不一樣 —— 用戶未來可能跳轉到其它頁面須要使用到的資源。若是 A 頁面發起一個 B 頁面的 prefetch
請求,這個資源獲取過程和導航請求多是同步進行的,而若是咱們用 preload
的話,頁面 A 離開時它會當即中止。
在 preload
和 prefetch
之間,咱們對當前頁面或即將跳轉的頁面在所需主要資源的問題有了一個解決方案。
<link rel="preload"> 和 <link rel="prefetch"> 的緩存行爲
當資源被 preload
或者 prefetch
後,會從網絡堆棧傳輸到 HTTP 緩存並進入渲染器的內存緩存。 若是資源能夠被緩存(例如,存在有效的 cache-control 和 max-age),它將存儲在 HTTP 緩存中,可用於當前和將來的會話。 若是資源不可緩存,則不會將其存儲在 HTTP 緩存中。 相反,它會被緩存到內存緩存中並保持不變直到它被使用。
Chrome 的網絡棧中是如何處理 preload 和 prefetch 的優先級?
下面是在 Blink 內核的 Chrome 46 及更高版本中不一樣資源的加載優先級狀況著做權歸做者全部。
preload 用 「as」 或者用 「type」 屬性來表示他們請求資源的優先級(好比說 preload 使用 as="style" 屬性將得到最高的優先級)。沒有 「as」 屬性的將被看做異步請求,「Early」意味着在全部未被預加載的圖片請求以前被請求(「late」意味着以後)
咱們來談一下這張表。
腳本根據它們在文件中的位置是否異步、延遲或阻塞得到不一樣的優先級:
- 網絡在第一個圖片資源以前阻塞的腳本在網絡優先級中是中級
- 網絡在第一個圖片資源以後阻塞的腳本在網絡優先級中是低級
- 異步/延遲/插入的腳本(不管在什麼位置)在網絡優先級中是很低級
圖像在可視窗口中比不在視口中的圖像(具備更高的優先級,所以在某種程度上, Chrome 將會盡可能懶加載這些不在視口中的圖片。 較低優先級的圖片出如今視口中時,該圖片的優先級就會獲得提高(可是注意已經在佈局完成後的圖片優先級不會在更改)。
使用「as」
屬性預加載的資源將具備與它們請求的資源類型相同的資源優先級。 例如,preload as =「style」
將得到最高優先級,而as =「script」
將得到低優先級或中優先級。 這些資源也遵循相同的CSP策略(例如腳本受 script-src
約束)。
不帶 「as」
屬性的 preload
的優先級將會等同於異步請求。
若是你想了解各類資源加載時的優先級屬性,從開發者工具的 Timeline/Performance
區域的 Network 區域都能看到相關信息:
在 Network 面板下的 「Priority」 部分
當頁面 preload 已經在 Service Worker 緩存及 HTTP 緩存中的資源時會發生什麼?
這各狀況來講是比較少的,但一般來講,會是比較好的狀況 —— 若是資源沒有超出 HTTP 緩存時間或者 Service Worker 沒有主動從新發起請求,那麼瀏覽器就不會再去請求這個資源了。
若是資源在 HTTP 緩存中(在SW緩存和網絡之間),那麼 preload
會從相同的資源中得到緩存命中。
這種加載方式會浪費用戶的帶寬嗎
使用 preload 或 prefetch,可能會浪費用戶的帶寬,特別是在資源沒有緩存的狀況下。
沒有用到的 preload
資源在 Chrome 的 console
裏會在 onload 事件 3s 後發生警告。
這個警告的緣由是,你可能正在使用preload
來嘗試爲其餘資源預加載並緩存以提升性能,可是若是這些預加載的資源沒有被使用,那麼你就在毫無理由地作額外的工做。在移動設備上,這至關於浪費用戶的流量,因此要注意預加載的內容。
什麼狀況會致使二次獲取?
preload
和 prefetch
是很簡單的工具,你很容易不當心二次獲取。
不要用 「prefetch」 做爲 「preload」 的後備方案 ,它們適用於不一樣的場景,經常會致使不符合預期的二次獲取。使用 preload
來獲取當前須要任務不然使用 prefetch
來獲取未來的任務,不要一塊兒用。
對 preload 使用 「as」 屬性,否則將不會從中獲益。
若是在指定要 preload
的內容(例如腳本)時未提供有效的「as」
,則最終將獲取兩次。
preload 字體不帶 crossorigin 也將會二次獲取, 確保在使用 preload
獲取字體時添加crossorigin
屬性,不然將二次下載。 他這個請求使用匿名的跨域模式。 即便字體與頁面位於同個域 下,也建議使用。也適用於其餘域名的獲取(好比說默認的異步獲取)。
最後,雖然它不會致使兩次獲取,但這一般是一個很好的建議:
不要全部的請求資源都加 preload,用 preload
來告訴瀏覽器一些很被須要的資源,以便讓它提前獲取它們。
我應當在頁面頭部全部的資源都加上 preload
?
這是工具的一個很好的例子,而不是規則。 preload
的文件數量取決於加載其餘資源時網絡內容、用戶的帶寬和其餘網絡情況。
儘早 preload
頁面中可能須要的文件,對於腳本,preload
你的密鑰包是很好的,由於它將獲取與執行分開,而僅僅使用 <script async>
不會這樣作,由於它會阻止窗口的 onload
事件。你能夠 preload
圖像、樣式、字體和媒體。最重要的是,做爲一名頁面做者,你能夠更好地控制提早獲取頁面所須要的信息。
prefetch 是否具備你應該注意的任何魔法屬性? 是的,
在 Chrome 中,若是用戶導航離開一個頁面,而對其餘頁面的預取請求仍在進行中,這些請求將不會被終止。
此外,不管資源的可緩存性如何,prefetch
請求在未指定的網絡堆棧緩存中至少保存 5 分鐘。
我在 JS 中使用自定義的 「preload」,它跟本來的 rel="preload" 或者 preload 頭部有什麼不一樣?
preload
解耦從 JS 處理和執行中獲取資源。 所以,preload 在標記中聲明以被 Chrome preload 掃描器掃描。 這意味着在許多狀況下,在 HTML 解析器甚至到達標籤以前,將獲取預加載(具備指示的優先級),這使它比自定義預加載實現更強大。
代碼部署後可能存在的BUG無法實時知道,過後爲了解決這些BUG,花了大量的時間進行log 調試,這邊順便給你們推薦一個好用的BUG監控工具 Fundebug。
不是能夠用 HTTP/2 的服務器推送來代替 preload 嗎?
當你知道資源的精確加載順序時使用推送,並讓 service worker 攔截可能致使再次推送緩存資源的請求。 使用 preload
可使資源的開始下載時間更接近初始請求 - 這對全部的資源獲取都有用。
咱們假設瀏覽器正在加載一個頁面,頁面中有個 CSS 文件,CSS 文件又引用一個字體庫,對於這樣的場景,
若使用 HTTP/2 PUSH,當服務端獲取到 HTML 文件後,知道之後客戶端會須要字體文件,它就當即主動地推送這個文件給客戶端,以下圖:
而對於 preload,服務端就不會主動地推送字體文件,在瀏覽器獲取到頁面以後發現 preload 字體纔會去獲取,以下圖:
雖然推送頗有效,但它不像 preload
那樣對全部的狀況都適應。
推送不能用於第三方資源的內容,經過當即發送資源,它還有效地縮短瀏覽器自身的資源優先級狀況。在你明確的知道在作什麼時,這應該會提升你的應用性能,若是不是很清晰的話,你也許會損失掉部分的性能。
peload 請求頭是什麼?它與 preload 標籤相好比何?它與 HTTP/2 服務器推送有什麼關係?
與其餘類型的連接同樣,preload 連接便可以使用 HTML標記 或 HTTP標頭。 在任何一種狀況下,preload 連接都會指示瀏覽器開始將資源加載到內存緩存中,這代表該頁面有很高可能性使用該資源,而且不但願等待預加載掃描程序或解析程序發現它。
當金融時報在它們的網站使用 preload HTTP 頭時,他們節約了大約 1s 的顯示片頭圖片時間。
1: 沒有使用 preload 2:使用了 preload
你可使用任何一種形式提供 preload 連接,可是你應該知道一個重要區別:如規範所容許的,許多服務器在遇到 HTTP 頭的 preload 連接時會觸發 HTTP/2 服務器推送。 HTTP/2 推送的性能影響不一樣於普通的預加載,因此你要確保沒有發起沒必要要的推送。
你可使用 preload 標籤來代替 preload 頭以免沒必要要的推送,或者在你的 HTTP 頭上加一個 「nopush」 屬性。
如何判斷 <link rel="preload"> 的支持狀況?
如下的代碼段能夠判斷 <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 讓CSS樣式當即生效嗎?
固然能夠,preload 支持基於異步加載的標記,使用 <link rel=」preload」>
的樣式表可使用 onload
事件當即應用於當前文檔:
<link rel="preload" href="style.css" onload="this.rel=stylesheet">
preload 還被哪些網站普遍的應用?
根據 HTTPArchive,大多數使用 <link rel =「preload」>
的網站使用它來預加載Web字體,包括 Teen Vogue 和前面提到的 Shopify:
而 LifeHacker 和 JCPenny 等其餘熱門網站使用它來異步加載CSS(經過Filament Group loadCSS):
而後,有愈來愈多的漸進式 Web 應用程序(如 Twitter.com mobile、Flipkart 和Housing)使用它來預加載當前導航所需的腳本(使用PRPL等模式)
其基本思想是以高粒度維護工件(而不是總體捆綁),因此任何應用均可以按需加載依賴或者預加載資源並放在緩存中。
當前瀏覽器對 preload 和 Prefetch 的支持程序如何
根據 CanIUse,<link rel =「preload」>
約 50% 的支持度, <link rel =「prefetch」>
約 71%。
相關閱讀
- Preload — what is it good for? — Yoav Weiss
- A study by the Chrome Data Saver team
- Planning for performance — Sam Saccone
- Webpack plugin for auto-wiring up <link rel="preload">
- What is preload, prefetch and preconnect? — KeyCDN
- Web Fonts preloaded by Zach Leat
- HTTP Caching: cache-control by Ilya Grigorik
關於Fundebug
Fundebug專一於JavaScript、微信小程序、微信小遊戲、支付寶小程序、React Native、Node.js和Java線上應用實時BUG監控。 自從2016年雙十一正式上線,Fundebug累計處理了10億+錯誤事件,付費客戶有Google、360、金山軟件、百姓網等衆多品牌企業。歡迎你們免費試用!