淺析Preload/Prefetch性能優化


  在網絡請求中,我們在使用到某些資源比如:圖片,JS,CSS 等等,在執行之前總需要等待資源的下載,如果我們能做到預先加載資源,那在資源執行的時候就不必等待網絡的開銷,這時候就輪到 preload 大顯身手的時候了。

一、preload 提前加載

  preload 是一個新的 Web 標准,在頁面生命周期中提前加載你指定的資源,同時確保在瀏覽器的主要渲染機制啟動之前。

  這樣就可以保證了其不會阻止瀏覽器的渲染並提前加載資源以此來提高性能。通常使用 preload 是用來加載圖片、CSS、JavaScript和字體文件。

1、語法:

  preload 的語法和加載 CSS 類似:

<link rel="stylesheet" href="styles/main.css"> // 加載CSS

  除了要把 rel 改為 preload ,同時還要指定 as 屬性,下面是一個簡單的例子

<head>
  <meta charset="utf-8">
  <title>JS and CSS preload example</title>

  <link rel="preload" href="style.css" as="style">
  <link rel="preload" href="main.js" as="script">

  <link rel="stylesheet" href="style.css">
</head>

<body>
  <h1>bouncing balls</h1>
  <canvas></canvas>

  <script src="main.js" defer></script>
</body>

  使用 as 來指定資源的類型可以使瀏覽器:

(1)更加精確地確定資源加載的優先級。

(2)將其存儲在緩存中以備將來使用,並在適當時重用資源。

(3)將正確的內容安全策略應用於資源。

(4)為其設置正確的 Accept 請求標頭。

  preload 支持如下資源的加載:audio、document、embed、fetch、font、image、object、script、style、track、worker、video(瀏覽器暫未實現)

  若你加載了一個 CORS 開啟的資源,必須要加上 crossorigin 屬性:

<link rel="preload" href="https://example.com/fonts/font.woff" as="font" crossorigin>

  除了使用 html 標簽來使用 preload,還可以使用如下方式:

<script>
var res = document.createElement("link"); res.rel = "preload"; res.as = "style"; res.href = "styles/other.css"; document.head.appendChild(res); </script>

  當瀏覽器解析到這行代碼就會去加載 href 中對應的資源但不執行,待到真正使用到的時候再執行,

  另一種方式方式就是在 HTTP 響應頭中加上 preload 字段:

Link: <https://example.com/other/styles.css>; rel=preload; as=style

  這種方式比通過 Link 方式加載資源方式更快,請求在返回還沒到解析頁面的時候就已經開始預加載資源了。

2、preload 的瀏覽器支持情況如下:

3、注意:

(1)瀏覽器使用 preload 加載資源的時候並沒有執行里面的代碼。比如 :

//myscript.js
alert(1) <link rel="preload" href="myscript.js" as="script">

  當使用瀏覽器加載時,並不會有彈窗。

(2)若下載時發生頁面導航,preload 進行中的資源會被取消。

二、prefetch 預判加載

  prefetch 是提示瀏覽器,用戶在下次導航時可能會使用的資源,因此瀏覽器為了提升性能可以提前加載、緩存資源。prefetch 的加載優先級相對較低,瀏覽器在空閑的時候才會在后台加載。

  prefetch 跟 preload 不同,它的作用是告訴瀏覽器未來可能會使用到的某個資源,瀏覽器就會在閑時去加載對應的資源,若能預測到用戶的行為,比如懶加載,點擊到其它頁面等則相當於提前預加載了需要的資源。

  它的用法跟 preload 是一樣的,也是3種:

// 1、link
<link rel="prefetch" href="/myscript.js" as="script">

// 2、JS
<script>
var res = document.createElement("link"); res.rel = "prefetch"; res.as = "style"; res.href = "styles/other.css"; document.head.appendChild(res); </script>

// 3、http header頭模式
Link: <https://example.com/other/styles.css>; rel=prefetch; as=style

  相對於 preload 而 prefetch 應用在一個不同的場景:跨導航、跨頁面使用資源,例如頁面 A 初始化了一個頁面 B 中關鍵資源的 prefetch 請求,則關鍵資源的加載和頁面導航可以並行執行,但若是 preload 它會在頁面 A 的unload 事件中立即取消。

  注意:

(1)prefetch 優先級較低,所以加載的資源有可能會被取消,例如:在網絡很差的時候,prefetch 一個大字體文件的時候就有可能被取消。

三、preload與prefetch的區別

1、當一個資源被 preload 或者 prefetch 獲取后,它將被放在內存緩存中等待被使用,如果資源未存在有效的緩存機制(如 cache-control 或 max-age),它將被存儲在 HTTP 緩存中可以被不同頁面所使用。

  正確使用 preload/prefetch 不會造成二次下載,也就說:當頁面上使用到這個資源時候 preload 資源還沒下載完,這時候不會造成二次下載,會等待第一次下載並執行腳本

  對於 preload 來說,一旦頁面關閉了,它就會立即停止 preload 獲取資源,而對於 prefetch 資源,即使頁面關閉,prefetch 發起的請求仍會進行不會中斷。

2、什么情況會導致二次獲取?

  不要將 preload 和 prefetch 進行混用,它們分別適用於不同的場景,對於同一個資源同時使用 preload 和 prefetch 會造成二次的下載。

  preload 字體不帶 crossorigin 也將會二次獲取! 確保你對 preload 的字體添加 crossorigin 屬性,否則他會被下載兩次,這個請求使用匿名的跨域模式。這個建議也適用於字體文件在相同域名下,也適用於其他域名的獲取(比如說默認的異步獲取)。

  preload 是告訴瀏覽器頁面必定需要的資源,瀏覽器一定會加載這些資源,而 prefetch 是告訴瀏覽器頁面可能需要的資源,瀏覽器不一定會加載這些資源。所以建議:對於當前頁面很有必要的資源使用 preload,對於可能在將來的頁面中使用的資源使用 prefetch。

3、這將會浪費用戶的帶寬嗎?

  用 “preload” 和 “prefetch” 情況下,如果資源不能被緩存,那么都有可能浪費一部分帶寬,在移動端請慎用。

  沒有用到的 preload 資源在 Chrome 的 console 里會在 onload 事件 3s 后發生警告。

The resource http://localhost:8082/js/index.js was preloaded using link preload but not used 
within a few seconds from the window’s load event.
Please make sure it has an appropriate as value and it is preloaded intentionally.

  原因是你可能為了改善性能使用 preload 來緩存一定的資源,但是如果沒有用到,你就做了無用功。在手機上,這相當於浪費了用戶的流量,所以明確你要 preload 對象。

4、不同資源瀏覽器優先級

  一個資源的加載的優先級被分為五個級別,分別是:

  • Highest 最高
  • High 高
  • Medium 中等
  • Low 低
  • Lowest 最低

(1)HTML/CSS 資源,其優先級是最高的

(2)font 字體資源,優先級分別為 Highest/High

(3)圖片資源,如果出現在視口中,則優先級為 High,否則為 Low

(4)而 script 腳本資源就比較特殊,優先級不一,腳本根據它們在文件中的位置是否異步、延遲或阻塞獲得不同的優先級:

  • 網絡在第一個圖片資源之前阻塞的腳本在網絡優先級中是 High
  • 網絡在第一個圖片資源之后阻塞的腳本在網絡優先級中是 Medium
  • 異步/延遲/插入的腳本(無論在什么位置)在網絡優先級中是 Low

  對於使用 prefetch 獲取資源,其優先級默認為最低,Lowest,可以認為當瀏覽器空閑的時候才會去獲取的資源。

  而對於 preload 獲取資源,可以通過 "as" 或者 "type" 屬性來標識他們請求資源的優先級(比如說 preload 使用 as="style" 屬性將獲得最高的優先級,即使資源不是樣式文件)

  沒有 “as” 屬性的將被看作異步請求。

5、與 async/defer 加載方式對比

  使用 async/defer 屬性在加載腳本的時候不阻塞 HTML 的解析,defer 加載腳本執行會在所有元素解析完成,DOMContentLoaded 事件觸發之前完成執行。它的用途其實跟 preload 十分相似。你可以使用 defer 加載腳本在 head 末尾,這比將腳本放在 body 底部效果來的更好。

(1)它相比於 preload 加載的優勢在於瀏覽器兼容性好,從 caniuse 上看基本上所有瀏覽器都支持,覆蓋率達到 93%,

(2)不足之處在於:defer 只作用於腳本文件,對於樣式、圖片等資源就無能為力了,並且 defer 加載的資源是要執行的,而 preload 則腳本、圖片、樣式都支持加載,且只下載資源並不執行,待真正使用到才會執行文件

(3)對於頁面上主/首屏腳本,可以直接使用 defer 加載,而對於非首屏腳本/其它資源,可以采用 preload/prefeth 來進行加載。

6、使用案例

(1)提前加載字體文件。由於字體文件必須等到 CSSOM 構建完成並且作用到頁面元素了才會開始加載,會導致頁面字體樣式閃動。所以要用 preload 顯式告訴瀏覽器提前加載。假如字體文件在 CSS 生效之前下載完成,則可以完全消滅頁面閃動效果。

(2)使用 preload 預加載第二屏的內容,在網頁開發中,對於非首屏部分采用懶加載是我們頁面常用的優化手段,所以我們在頁面 onload 之后可以通過 preload 來加載次屏所需要的資源,在用戶瀏覽完首屏內容滾動時能夠更快地看到次屏的內容。

(3)在頁面加載完成之后,可以分析頁面上所有的鏈接,判斷用戶可能會點擊的頁面,分析提取下一跳頁面上所有的資源使用 prefetch 進行加載(這里不使用 preload,因為不一定會點擊),瀏覽器會在空閑地時候進行加載,當用戶點擊鏈接命中了緩存,這可以有效地提升下一頁面的首屏渲染時間。

(4)對於商品列表頁面,在用戶鼠標停留在某個商品的時候,可以去分析商品詳情頁所需要的資源並提前開啟 preload 加載,跟第 3 點類似,都是用來預測用戶的行為並且做出一些預加載的手段,區別在於當用戶停留在商品上時,點擊命中率更高,preload 可以立即加載資源,有效提升緩存命中率。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM