最近的項目中爲了可以提高那麼一丟丟性能,嘗試了一下對 chunks
進行預加載處理。雖然作了異步加載的處理,可是項目大小決定了仍是有多個異步的 chunk.js
須要進行預加載,這裏我指的是 preload
與
prefetch
。css
這裏推薦一個 GoogleChromeLabs
團隊推出的插件:
preload-webpack-plugin
。html
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
<link rel='prefetch'>
出現的更早,瀏覽器支持度也挺不錯,通俗的理解是着眼於下一個頁面資源的預加載,因此在當前頁面的優先級是很低的。
prefetch
在瀏覽器空閒時間下載或預取用戶在不久的未來可能訪問的文檔,在當前的頁面加載完成後預取資源放進緩存中,在以後的調用就直接從緩存中獲取,從而提高性能。
<link rel='preload' as=''>
則是着眼於如今(當前頁面)。瀏覽器遇到 rel= 'preload' 的標籤就會將其推入到預加載器中,這個預加載器也將用於其餘咱們所須要的,各類各樣的,任意類型的資源。爲了完成基本的配置,你還須要經過 href
和as
屬性指定須要被預加載資源的資源路徑及其類型。
引用一段 MDN 的描述:
<link>
元素的rel
屬性的屬性值preload
可以讓你在你的HTML頁面中<head>
元素內部書寫一些聲明式的資源獲取請求,能夠指明哪些資源是在頁面加載完成後即刻須要的。對於這種即刻須要的資源,你可能但願在頁面加載的生命週期的早期階段就開始獲取,在瀏覽器的主渲染機制介入前就進行預加載。這一機制使得資源能夠更早的獲得加載並可用,且更不易阻塞頁面的初步渲染,進而提高性能。本文提供了一個如何有效使用preload
機制的基本說明
其特色是 聲明式的資源獲取請求(fetch)、不易(注意不是不會)阻塞 onLoad、as 提供的細粒度的控制
總結其優點:
Accept
請求頭。注意:忽略 as 屬性,或者錯誤的 as 屬性會使 preload 等同於 XHR 請求,瀏覽器不知道加載的是什麼,所以會賦予此類資源很是低的加載優先級。
preload
還支持 onload
預加載完成回調、MIME、跨域獲取、響應式的預加載、腳本化加載等,更多能夠參考 MDN,以及這裏,還有這裏。
既然 preload
以及 prefetch
都是優先從 HTTP
緩存中獲取資源,咱們必然要接觸不少 304 Not Modified
響應。
咱們先來看一個 304 響應:
304 中最重要的兩個請求頭就是 If-None-Match
和 If-Modified-Since
。
前者的值是上一次響應中返回的 ETag
,ETag
是對該資源的一種惟一標識,只要資源有變化,Etag
就會從新生成。服務器接收到請求頭中的 If-None-Match
以後就和文件資源的 Etag
作比較,若是同樣,說明資源沒有變化,就返回一個 304 Not Modified
而且沒有響應體。客戶端收到 304 響應後,就會從緩存中讀取對應的資源,若是不相同,則表示資源發生了改變,那麼服務器就會返回 HTTP/200 OK
響應,響應體就是該資源當前最新的內容.客戶端收到 200 響應後,就會用新的響應體覆蓋掉舊的緩存資源。
後者對應的上一次響應返回的 Last-Modified
。Last-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 分鐘內再次加載資源,就會命中強緩存。
用戶行爲對瀏覽器緩存的控制:
未完待續....