摘要: 理解Preload與Prefetch。
Fundebug經授權轉載,版權歸原作者所有。
這是 Web 效能優化的第 6 篇,上一篇在下面看點選檢視:
- Web 效能優化:使用 Webpack 分離資料的正確方法
- Web 效能優化:圖片優化讓網站大小減少 62%
- Web 效能優化:快取 React 事件來提高效能
- Web 效能優化:21種優化CSS和加快網站速度的方法
- Web 效能優化:理解及使用 JavaScript 快取
今天,我們將深入研究Chrome 的網路棧,以明確 web 載入原語(如<link rel= preload >
& <link rel= prefetch >
) 背後的工作原理,以便你能夠更有效地使用它們。
如其他文章所述,preload 是一個宣告式 fetch
,可以強制瀏覽器在不阻塞 document
的 onload 事件的情況下請求資源。
Prefetch
告訴瀏覽器這個資源將來可能需要,但是什麼時間載入這個資源是由瀏覽器來決定的。
在預載入(perload)之前,網路請求從這裡開始,預載入之後,它在解析時從左向右移動
使用預載入(perload)的一些案例
在詳細介紹 預載入(perload) 之前,先來看看一些使用 預載入(perload) 的案例。
Housing.com 在對他們的漸進式 Web 應用程式的指令碼轉用 proload 看到大約縮短了10%的可互動時間。
Shopify 使用 [preload 載入 Web字型](www.bramstein.com/writing/pre… nts.html)後,Chrome 桌面版)的文字繪製時間(1.2秒)提高了50%,這完全解決了他們的文字閃動問題。
左邊:使用 preload,右邊:不使用 preload
使用 載入字型
Treebo,印度最大的旅館網站之一,在 3G 網路下對其桌面版試驗,在對其頂部圖片和主要的 Webpack 打包檔案使用 preload
之後,在首屏繪製和可互動延遲分別減少了 1s。
同樣的,在對自己的漸進式 Web 應用程式主要打包檔案使用 preload 之後,Flipkart 在路由解析之前 節省了大量的主執行緒空閒時間(在 3G 網路下的低效能手機下)。
上面:沒有使用 proload 載入,下面:使用 preload 載入
Chrome 資料保護程式團隊發現,對於那些可以在指令碼和 CSS 樣式表上使用 preload
的頁面,發現頁面首次繪製時間獲得平均 12% 的速度提升。
對於 prefetch(預讀取)
,它被廣泛使用,在 Google 我們仍用它來預讀取
一些可以加快 搜尋結果頁面 的渲染的關鍵資源。
Preload
在大型網站中都有很好運用,你可以在本文後面找到更多這些安全。 在此之前,讓我們深入瞭解網路堆疊如何實際處理 預載入(prefetch)與預讀取(prefetch)。
何時使用 和 ?
提示:
preload
載入資源一般是當前頁面需要的,prefetch
一般是其它頁面有可能用到的資源。
preload
是告訴瀏覽器預先請求當前頁面需要的資源(關鍵的指令碼,字型,主要圖片等)。
prefetch
應用場景稍微又些不同 —— 使用者將來可能跳轉到其它頁面需要使用到的資源。如果 A 頁面發起一個 B 頁面的 prefetch
請求,這個資源獲取過程和導航請求可能是同步進行的,而如果我們用 preload
的話,頁面 A 離開時它會立即停止。
在 preload
和 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”>
支援情況:
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
- 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、金山軟體、百姓網等眾多品牌企業。歡迎大家免費試用!