預加載與緩存

最近的項目中爲了可以提高那麼一丟丟性能,嘗試了一下對 chunks 進行預加載處理。雖然作了異步加載的處理,可是項目大小決定了仍是有多個異步的 chunk.js 須要進行預加載,這裏我指的是 preload prefetchcss

這裏推薦一個 GoogleChromeLabs 團隊推出的插件:preload-webpack-pluginhtml

A webpack plugin for injecting <link rel='preload|prefecth'> into HtmlWebpackPlugin pages, with async chunk support


使用大體以下,具體參考其文檔:前端

config = rewirePreloadPlugin(config, env, {  
  rel: 'preload',  
  include: 'initial',  
  as(entry) {    
    if (/\.(css|less)$/.test(entry)) return 'style';    
    if (/\.woff$/.test(entry)) return 'font';    
    if (/\.(png|jpg|jpeg|svg)$/.test(entry)) return 'image';    
    return 'script';  
  }});
複製代碼

這個插件對字體元素( woff ),自動加了跨域:react

源碼:webpack

const crossOrigin = asValue === 'font' ? 'crossorigin="crossorigin" ' : '';

filesToInclude+= `<link rel="${options.rel}" as="${asValue}" ${crossOrigin}href="${entry}">\n`複製代碼

使用後:git


將會在你的 index.html 中的 Head 標籤裏面添加一堆 <link rel='preload' as='script' href='xxx.chunk.js'>github

這就意味着這些資源都會被預加載,而後從緩存中去獲取。將會提高網頁打開的效率,可是濫用也會浪費帶寬。web

實際使用中有一個問題,就是我使用的是 create-react-app 而且沒有 eject 的狀況下,目前這個插件對於 preload 的模式包括 include: 'initial' | 'allChunks' | 'asyncChunks' |'allAssets' | ['chunkName'],(ps: initial 模式仍是尤雨溪大大提的 PR )而且提供了黑白名單,應該說是很細粒度的控制咱們去按需設置文件爲 preload 的。chrome

可是實際上仍是會形成有我不須要其 preload 的文件被設置了,這就形成了浪費。express

Preload 與 Prefetch

<link rel='prefetch'> 出現的更早,瀏覽器支持度也挺不錯,通俗的理解是着眼於下一個頁面資源的預加載,因此在當前頁面的優先級是很低的。

prefetch 在瀏覽器空閒時間下載或預取用戶在不久的未來可能訪問的文檔,在當前的頁面加載完成後預取資源放進緩存中,在以後的調用就直接從緩存中獲取,從而提高性能。

<link rel='preload' as=''> 則是着眼於如今(當前頁面)。瀏覽器遇到 rel= 'preload' 的標籤就會將其推入到預加載器中,這個預加載器也將用於其餘咱們所須要的,各類各樣的,任意類型的資源。爲了完成基本的配置,你還須要經過 hrefas 屬性指定須要被預加載資源的資源路徑及其類型。

引用一段 MDN 的描述:

<link> 元素的 rel 屬性的屬性值 preload可以讓你在你的HTML頁面中 <head>元素內部書寫一些聲明式的資源獲取請求,能夠指明哪些資源是在頁面加載完成後即刻須要的。對於這種即刻須要的資源,你可能但願在頁面加載的生命週期的早期階段就開始獲取,在瀏覽器的主渲染機制介入前就進行預加載。這一機制使得資源能夠更早的獲得加載並可用,且更不易阻塞頁面的初步渲染,進而提高性能。本文提供了一個如何有效使用 preload機制的基本說明

其特色是 聲明式的資源獲取請求(fetch)、不易(注意不是不會)阻塞 onLoad、as 提供的細粒度的控制

總結其優點:

  1. 更精確地優化資源加載優先級,這種方式能夠確保資源根據其重要性依次加載。
  2. 匹配將來的加載需求,在適當的狀況下(相同的資源),重複利用同一資源。
  3. 爲資源應用正確的內容安全策略
  4. 爲資源設置正確的 Accept 請求頭。

注意:忽略 as 屬性,或者錯誤的 as 屬性會使 preload 等同於 XHR 請求,瀏覽器不知道加載的是什麼,所以會賦予此類資源很是低的加載優先級。

preload 還支持 onload 預加載完成回調、MIME、跨域獲取、響應式的預加載、腳本化加載等,更多能夠參考 MDN,以及這裏,還有這裏

緩存

既然 preload 以及 prefetch 都是優先從 HTTP 緩存中獲取資源,咱們必然要接觸不少 304 Not Modified 響應。

咱們先來看一個 304 響應:


304 中最重要的兩個請求頭就是 If-None-MatchIf-Modified-Since

前者的值是上一次響應中返回的 ETagETag 是對該資源的一種惟一標識,只要資源有變化,Etag 就會從新生成。服務器接收到請求頭中的 If-None-Match 以後就和文件資源的 Etag 作比較,若是同樣,說明資源沒有變化,就返回一個 304 Not Modified 而且沒有響應體。客戶端收到 304 響應後,就會從緩存中讀取對應的資源,若是不相同,則表示資源發生了改變,那麼服務器就會返回 HTTP/200 OK 響應,響應體就是該資源當前最新的內容.客戶端收到 200 響應後,就會用新的響應體覆蓋掉舊的緩存資源。

後者對應的上一次響應返回的 Last-ModifiedLast-Modified 是該資源文件最後一次更改時間,服務器會在 response header 裏返回,同時瀏覽器會將這個值保存起來,在下一次發送請求時,放到 request header 裏的 If-Modified-Since 裏,服務器在接收到後也會作比對,若是沒過時則返回304,若是過時則返回200 ok。一樣會更新舊的文件。

若是兩個頭部都不帶的請求就是無條件(unconditionally)請求該資源,服務器也就必須返回完整的資源數據。

上面所述的幾個頭部就是協商緩存的關鍵人物了。

這裏還要說明一個概念就是條件請求,所謂條件就是沒法肯定前端緩存資源是否最新,因此經過上述的頭部來作一個驗證,返回 304 或者 200。經過條件請求咱們能夠節省出一個響應體,可是請求仍是發出去了。

這裏若是連請求都不想發出去怎麼辦呢?

強緩存、協商緩存

瀏覽器緩存主要有兩類:緩存協商和完全緩存,也有稱之爲協商緩存強緩存

1.強緩存:不會向服務器發送請求,直接從緩存中讀取資源,在chrome控制檯的network選項中能夠看到該請求返回200的狀態碼;

2.協商緩存:向服務器發送請求,服務器會根據這個請求的request header的一些參數來判斷是否命中協商緩存,若是命中,則返回304狀態碼並帶上新的response header通知瀏覽器從緩存中讀取資源;

二者的共同點是,都是從客戶端緩存中讀取資源;區別是強緩存不會發請求,協商緩存會發請求。

瀏覽器緩存流程圖:


因此咱們能夠經過設置 Expires( HTTP1.0 ) 以及 Cache-Control( HTTP1.1 ),來命中強緩存,從而跳過發送請求的過程。

Expires:response header裏的過時時間,瀏覽器再次加載資源時,若是在這個過時時間內,則命中強緩存。

Cache-Control:當值設爲max-age= 600 時,則表明在這個請求正確返回時間(瀏覽器也會記錄下來)的 10 分鐘內再次加載資源,就會命中強緩存。

用戶行爲對瀏覽器緩存的控制:

  1. F5 刷新,瀏覽器會設置max-age=0,跳過強緩存判斷,會進行協商緩存判斷。
  2. Ctrl + F5, 跳過協商緩存與強緩存,直接從服務器拉取資源。
  3. 地址欄訪問,連接跳轉是正經常使用戶行爲,將會觸發瀏覽器緩存機制。

未完待續....

相關文章
相關標籤/搜索