瀏覽器緩存分爲兩種類型:css
瀏覽器緩存機制的過程以下:html
強緩存是最完全的緩存,無需向服務器發送請求,一般用於css、js、圖片等靜態資源。瀏覽器發送請求後會先判斷本地是否有緩存。若是無緩存,則直接向服務器發送請求;若是有緩存,則判斷緩存是否命中強緩存,若是命中則直接使用本地緩存,若是沒命中則向服務器發送請求。判斷是否命中本地緩存的方法有兩種:Expires和Cache-Control。webpack
Expires是http1.0的響應頭,表明的含義是資源本地緩存的過時時間,由服務器設定。服務器返回給瀏覽器的響應頭中若是包含Expires字段,瀏覽器發送請求時拿當前時間和Expires字段值進行比較,判斷資源緩存是否失效。以下圖所示:web
Date表明請求資源的時間,Expires表明資源緩存的過時時間,能夠看到服務器設置資源的緩存時間爲5分鐘。2017-02-10 07:53:19以前,請求這個資源就是命中本地緩存。超過這個時間再去請求則不命中。瀏覽器
Cache-Control是http1.0中新增的字段。因爲Expires設置的是資源的具體過時時間,若是服務器時間和客戶端時間不同,就會形成緩存錯亂,好比認爲調節了客戶端的時間,因此設置資源有效期的時長更合理。http1.1添加了Cache-Control的max-age字段。max-age表明的含義是資源有效期的時長,是一個相對時長,單位爲s。緩存
Cache-Control: max-age = 300設置資源的過時時間爲5分鐘。瀏覽器再次發送請求時,會把第一次請求的時間和max-age字段值相加和當前時間比較,以此判斷是否命中本地緩存。max-age使用的都是客戶端時間,比Expires更可靠。若是max-age和Expires同時出現,max-age的優先級更高。Cache-Control提供了更多的字段來控制緩存:服務器
協商緩存的判斷在服務器端進行,判斷是否命中的依據就是此次請求和上次請求之間資源是否發生改變。未發生改變命中,發生改變則未命中。判斷文件是否發生改變的方法有兩個:Last-Modified、If-Modified-Since和Etag、If-None-Match。app
Last-Modified是http1.0中的響應頭字段,表明請求的資源最後一次的改變時間。If-Modified-Since是http1.0的請求頭,If-Modified-Since的值是上次請求服務器返回的Last-Modified的值。瀏覽器第一次請求資源時,服務器返回Last-Modified,瀏覽器緩存該值。瀏覽器第二次請求資源時,用於緩存的Last-Modified賦值給If-Modified-Since,發送給服務器。服務器判斷If-Modified-Since和服務器本地的Last-Modified是否相等。若是相等,說明資源未發生改變,命中協商緩存;若是不相等,說明資源發生改變,未命中協商緩存。webpack-dev-server
能夠看到該請求返回的是304狀態碼,說明資源的Last-Modified未改變,因此此次請求的Last-Modified和If-Modified-Since是一致的。測試
Last-Modified、If-Modified-Since使用的都是服務器提供的時間,因此相對來講仍是很可靠的。可是因爲修改時間的精確級別或者按期生成文件這種狀況,會形成必定的錯誤。因此http1.1添加Etag、If-None-Match字段,完善協商緩存的判斷。Etag是根據資源文件內容生成的資源惟一標識符,一旦資源內容發生改變,Etag就會發生改變。基於內容的標識符比基於修改時間的更可靠。If-None-Match的值是上次請求服務器返回的Etag的值。Etag、If-None-Match的判斷過程和Last-Modified、If-Modified-Since一致,Etag、If-None-Match的優先級更高。
強緩存的優點很明顯,無需向服務器發送請求,節省了大量的時間和帶寬。可是有一個問題,緩存有效期內想更新資源怎麼辦?我在工程中還遇到另一個問題,一個項目有四個環境,測試環境、開發環境、在線確認環境、在線環境,四個環境的域名相同,這樣就會形成四個環境的緩存共用問題。好比先訪問了測試環境,index.js被換成到瀏覽器中,再切換到在線環境,在線環境會請求index.js,此時瀏覽器就會使用本地緩存中測試環境的index.js,形成代碼錯亂。
如何使強緩存失效,是問題的關鍵。一般的解決方法是更新文件名,文件名不同的話,瀏覽器就會從新請求資源。咱們要保證新發布版本和不一樣環境中的文件名是不同的。其中一種方法在文件名後加版本號:
index.js?version=1
index.css?version=1
複製代碼
webpack提供了很簡單的方法能夠配置緩存。
// webpack.config.js
module.exports = {
entry: "main.js",
output: {
path: "/build",
filename: "main.[hash].js"
}
};
複製代碼
經過hash佔位符,在每次生成打包文件時,都會經過文件內容生成惟一的hash,並添加到輸出的文件名中。若是有多個入口文件,能夠使用name佔位符設置輸出:
// webpack.config.js
module.exports = {
entry: {
main:"main.js",
sub:"sub.js"
},
output: {
path: "/dist",
filename: "[name].[hash].js"
}
};
複製代碼
這時候有一個問題是,此時的hash是根據兩個文件的內容來生成的,兩個文件名使用的hash是一致的。若是main.js和sub.js只有一個改變,兩個文件名都會改變,兩個文件都會從新請求,形成資源浪費。webpack提供了chunkhash來代替hash在多入口狀況下使用。chunkhash是根據每一個入口文件單獨生成的哈希值,避免上述狀況。
webpack打包動態生成文件名,咱們須要動態地把文件引用插入到html啓動文件中。html-webpack-plugin能夠幫我很好地解決這個問題。html-webpack-plugin
能夠動態地生成一個html文件,並在html文件中動態插入webpack打包生成的資源文件。
var HtmlWebpackPlugin = require('html-webpack-plugin');
var webpackConfig = {
entry: 'main.js',
output: {
path: '/dist',
publicPath: '/dist',
filename: 'main.[hash].js'
},
plugins: [new HtmlWebpackPlugin()]
};
複製代碼
默認在webpackConfig.output.path
路徑下生成index.html
,生成的html文件以下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Webpack App</title>
</head>
<body>
<script src="main.2a6c1fee4b5b0d2c9285.js"></script>
</body>
</html>
複製代碼
一般html啓動文件都有自定義的內容,因此html-webpack-plugin
提供了模板功能,template字段設置模板的路徑,html-webpack-plugin
以template爲模板,動態添加webpack打包生成的資源路徑。
var HtmlWebpackPlugin = require('html-webpack-plugin');
var webpackConfig = {
entry: 'main.js',
output: {
path: '/dist',
publicPath: '/dist',
filename: 'main.[hash].js'
},
plugins: [new HtmlWebpackPlugin(
{
template:'index.html'
}
)]
};
複製代碼
原index.html內容(\index.html):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>stat-front</title>
<link rel="stylesheet" href="//at.alicdn.com/t/font_ejl5slgdvtg74x6r.css">
</head>
<body>
<div id="app" class="app-root">
<router-view></router-view>
</div>
<!-- built files will be auto injected -->
</body>
</html>
複製代碼
生成的index.html內容(\dist\index.html):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>stat-front</title>
<link rel="stylesheet" href="//at.alicdn.com/t/font_ejl5slgdvtg74x6r.css">
</head>
<body>
<div id="app" class="app-root">
<router-view></router-view>
</div>
<!-- built files will be auto injected -->
<script src="main.2a6c1fee4b5b0d2c9285.js"></script>
</body>
</html>
複製代碼
最開始的時候靜態的index.html在根目錄下,webpack-dev-server
設置的啓動路徑就是根目錄下的index.html,若是要啓動生成的index.html,還須要設置webpackConfig.output.publicPath
:
var HtmlWebpackPlugin = require('html-webpack-plugin');
var webpackConfig = {
entry: 'main.js',
output: {
path: '/dist',
publicPath: '/',
filename: 'main.[hash].js'
},
plugins: [new HtmlWebpackPlugin(
{
template:'index.html'
}
)]
};
複製代碼
這樣webpack-dev-server在內存中生成的資源都存放在根目錄下,生成的index.html會代替原index.html啓動。