詳解瀏覽器緩存和webpack緩存配置
瀏覽器緩存
瀏覽器緩存分為兩種類型:
- 強(qiáng)緩存:也稱為本地緩存,不向服務(wù)器發(fā)送請求,直接使用客戶端本地緩存數(shù)據(jù)
- 協(xié)商緩存:也稱304緩存,向服務(wù)器發(fā)送請求,由服務(wù)器判斷請求文件是否發(fā)生改變。如果未發(fā)生改變,則返回304狀態(tài)碼,通知客戶端直接使用本地緩存;如果發(fā)生改變,則直接返回請求文件。
瀏覽器緩存機(jī)制的過程如下:

強(qiáng)緩存(本地緩存)
強(qiáng)緩存是最徹底的緩存,無需向服務(wù)器發(fā)送請求,通常用于css、js、圖片等靜態(tài)資源。瀏覽器發(fā)送請求后會先判斷本地是否有緩存。如果無緩存,則直接向服務(wù)器發(fā)送請求;如果有緩存,則判斷緩存是否命中強(qiáng)緩存,如果命中則直接使用本地緩存,如果沒命中則向服務(wù)器發(fā)送請求。判斷是否命中本地緩存的方法有兩種: Expires 和 Cache-Control 。
Expires
Expires是http1.0的響應(yīng)頭,代表的含義是資源本地緩存的過期時(shí)間,由服務(wù)器設(shè)定。服務(wù)器返回給瀏覽器的響應(yīng)頭中如果包含Expires字段,瀏覽器發(fā)送請求時(shí)拿當(dāng)前時(shí)間和Expires字段值進(jìn)行比較,判斷資源緩存是否失效。如下圖所示:
Date 代表請求資源的時(shí)間, Expires 代表資源緩存的過期時(shí)間,可以看到服務(wù)器設(shè)置資源的緩存時(shí)間為5分鐘。2017-02-10 07:53:19之前,請求這個(gè)資源就是命中本地緩存。超過這個(gè)時(shí)間再去請求則不命中。
Cache-Control
Cache-Control是http1.0中新增的字段。由于Expires設(shè)置的是資源的具體過期時(shí)間,如果服務(wù)器時(shí)間和客戶端時(shí)間不一樣,就會造成緩存錯(cuò)亂,比如認(rèn)為調(diào)節(jié)了客戶端的時(shí)間,所以設(shè)置資源有效期的時(shí)長更合理。http1.1添加了Cache-Control的max-age字段。max-age代表的含義是資源有效期的時(shí)長,是一個(gè)相對時(shí)長,單位為s。
Cache-Control: max-age = 300設(shè)置資源的過期時(shí)間為5分鐘。瀏覽器再次發(fā)送請求時(shí),會把第一次請求的時(shí)間和max-age字段值相加和當(dāng)前時(shí)間比較,以此判斷是否命中本地緩存。max-age使用的都是客戶端時(shí)間,比Expires更可靠。如果max-age和Expires同時(shí)出現(xiàn),max-age的優(yōu)先級更高。Cache-Control提供了更多的字段來控制緩存:
- no-store,不判斷強(qiáng)緩存和協(xié)商緩存,服務(wù)器直接返回完整資源
- no-cache,不判斷強(qiáng)緩存,每次都需要向?yàn)g覽器發(fā)送請求,進(jìn)行協(xié)商緩存判斷
- public,指示響應(yīng)可被任何緩存區(qū)緩存
- private,通常只為單個(gè)用戶緩存,不允許任何共享緩存對其進(jìn)行緩存,通常用于用戶個(gè)人信息
協(xié)商緩存
協(xié)商緩存的判斷在服務(wù)器端進(jìn)行,判斷是否命中的依據(jù)就是這次請求和上次請求之間資源是否發(fā)生改變。未發(fā)生改變命中,發(fā)生改變則未命中。判斷文件是否發(fā)生改變的方法有兩個(gè): Last-Modified、If-Modified-Since 和 Etag、If-None-Match 。
Last-Modified、If-Modified-Since
Last-Modified是http1.0中的響應(yīng)頭字段,代表請求的資源最后一次的改變時(shí)間。If-Modified-Since是http1.0的請求頭,If-Modified-Since的值是上次請求服務(wù)器返回的Last-Modified的值。瀏覽器第一次請求資源時(shí),服務(wù)器返回Last-Modified,瀏覽器緩存該值。瀏覽器第二次請求資源時(shí),用于緩存的Last-Modified賦值給If-Modified-Since,發(fā)送給服務(wù)器。服務(wù)器判斷If-Modified-Since和服務(wù)器本地的Last-Modified是否相等。如果相等,說明資源未發(fā)生改變,命中協(xié)商緩存;如果不相等,說明資源發(fā)生改變,未命中協(xié)商緩存。
可以看到該請求返回的是304狀態(tài)碼,說明資源的Last-Modified未改變,所以這次請求的Last-Modified和If-Modified-Since是一致的。
Etag、If-None-Match
Last-Modified、If-Modified-Since使用的都是服務(wù)器提供的時(shí)間,所以相對來說還是很可靠的。但是由于修改時(shí)間的精確級別或者定期生成文件這種情況,會造成一定的錯(cuò)誤。所以http1.1添加Etag、If-None-Match字段,完善協(xié)商緩存的判斷。Etag是根據(jù)資源文件內(nèi)容生成的資源唯一標(biāo)識符,一旦資源內(nèi)容發(fā)生改變,Etag就會發(fā)生改變?;趦?nèi)容的標(biāo)識符比基于修改時(shí)間的更可靠。If-None-Match的值是上次請求服務(wù)器返回的Etag的值。Etag、If-None-Match的判斷過程和Last-Modified、If-Modified-Since一致,Etag、If-None-Match的優(yōu)先級更高。
工程中遇到的問題
強(qiáng)緩存的優(yōu)勢很明顯,無需向服務(wù)器發(fā)送請求,節(jié)省了大量的時(shí)間和帶寬。但是有一個(gè)問題,緩存有效期內(nèi)想更新資源怎么辦?我在工程中還遇到另外一個(gè)問題,一個(gè)項(xiàng)目有四個(gè)環(huán)境,測試環(huán)境、開發(fā)環(huán)境、在線確認(rèn)環(huán)境、在線環(huán)境,四個(gè)環(huán)境的域名相同,這樣就會造成四個(gè)環(huán)境的緩存共用問題。比如先訪問了測試環(huán)境,index.js被換成到瀏覽器中,再切換到在線環(huán)境,在線環(huán)境會請求index.js,此時(shí)瀏覽器就會使用本地緩存中測試環(huán)境的index.js,造成代碼錯(cuò)亂。
如何使強(qiáng)緩存失效,是問題的關(guān)鍵。通常的解決方法是更新文件名,文件名不一樣的話,瀏覽器就會重新請求資源。我們要保證新發(fā)布版本和不同環(huán)境中的文件名是不一樣的。其中一種方法在文件名后加版本號:
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占位符,在每次生成打包文件時(shí),都會通過文件內(nèi)容生成唯一的hash,并添加到輸出的文件名中。如果有多個(gè)入口文件,可以使用name占位符設(shè)置輸出:
// webpack.config.js
module.exports = {
entry: {
main:"main.js",
sub:"sub.js"
},
output: {
path: "/dist",
filename: "[name].[hash].js"
}
};
這時(shí)候有一個(gè)問題是,此時(shí)的hash是根據(jù)兩個(gè)文件的內(nèi)容來生成的,兩個(gè)文件名使用的hash是一致的。如果main.js和sub.js只有一個(gè)改變,兩個(gè)文件名都會改變,兩個(gè)文件都會重新請求,造成資源浪費(fèi)。webpack提供了chunkhash來代替hash在多入口情況下使用。chunkhash是根據(jù)每個(gè)入口文件單獨(dú)生成的哈希值,避免上述情況。
webpack打包動態(tài)生成文件名,我們需要動態(tài)地把文件引用插入到html啟動文件中。 html-webpack-plugin 可以幫我很好地解決這個(gè)問題。 html-webpack-plugin 可以動態(tài)地生成一個(gè)html文件,并在html文件中動態(tài)插入webpack打包生成的資源文件。
var HtmlWebpackPlugin = require('html-webpack-plugin');
var webpackConfig = {
entry: 'main.js',
output: {
path: '/dist',
publicPath: '/dist',
filename: 'main.[hash].js'
},
plugins: [new HtmlWebpackPlugin()]
};
默認(rèn)在 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啟動文件都有自定義的內(nèi)容,所以 html-webpack-plugin 提供了模板功能,template字段設(shè)置模板的路徑, html-webpack-plugin 以template為模板,動態(tài)添加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內(nèi)容(\index.html):
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>stat-front</title> <link rel="stylesheet" rel="external nofollow" rel="external nofollow" > </head> <body> <div id="app" class="app-root"> <router-view></router-view> </div> <!-- built files will be auto injected --> </body> </html>
生成的index.html內(nèi)容(\dist\index.html):
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>stat-front</title> <link rel="stylesheet" rel="external nofollow" rel="external nofollow" > </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>
最開始的時(shí)候靜態(tài)的index.html在根目錄下, webpack-dev-server 設(shè)置的啟動路徑就是根目錄下的index.html,如果要啟動生成的index.html,還需要設(shè)置 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在內(nèi)存中生成的資源都存放在根目錄下,生成的index.html會代替原index.html啟動。
總結(jié)
以上所述是小編給大家介紹的瀏覽器緩存和webpack緩存配置,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時(shí)回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
Javascript實(shí)現(xiàn)圖片輪播效果(二)圖片序列節(jié)點(diǎn)的控制實(shí)現(xiàn)
這篇文章主要介紹了Javascript實(shí)現(xiàn)圖片輪播效果(二)圖片序列節(jié)點(diǎn)的控制實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下2016-02-02
js字符串替換所有的指定字符或文字(推薦replaceAll方法)
要實(shí)現(xiàn)js字符串替換所有的某個(gè)字符,推薦大家使用replaceAll方法,默認(rèn)不是所有瀏覽器都兼容,所以這里給出一個(gè)解決方案,需要的朋友可以參考下2014-07-07
移動端使用localStorage緩存Js和css文的方法(web開發(fā))
這篇文章主要介紹了web移動端使用localStorage緩存Js和css文的方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-09-09
JavaScript數(shù)據(jù)類型的存儲方法詳解
JavaScript中基本數(shù)據(jù)類型和引用數(shù)據(jù)類型是如何存儲的呢?下面通過本文給大家分享js數(shù)據(jù)類型的存儲方法,需要的朋友參考下吧2017-08-08
根據(jù)鼠標(biāo)的位置動態(tài)的控制層的位置
根據(jù)鼠標(biāo)的位置動態(tài)的設(shè)置層的位置的js代碼。2009-11-11
微信小程序如何修改本地緩存key中單個(gè)數(shù)據(jù)的詳解
這篇文章主要介紹了微信小程序如何修改本地緩存key中單個(gè)數(shù)據(jù),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
Javascript立即執(zhí)行函數(shù)(IIFE)實(shí)例詳解
IIFE全拼Imdiately?Invoked?Function?Expression,是一個(gè)在定義的時(shí)候就立即執(zhí)行的JavaScript函數(shù),這篇文章主要給大家介紹了關(guān)于Javascript立即執(zhí)行函數(shù)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-04-04
moment.js輕松實(shí)現(xiàn)獲取當(dāng)前日期是當(dāng)年的第幾周
這篇文章主要介紹了moment.js輕松實(shí)現(xiàn)獲取當(dāng)前日期是當(dāng)年的第幾周,需要的朋友可以參考下2015-02-02

