开发者来讲,浏览器充当了重要角色。浏览器缓存(Brower Caching)是浏览器在本地磁盘对用户最近请求过的文档进行存储,当访问者再次访问同一页面时,浏览器就能够直接从本地磁盘加载文档。浏览器缓存的优势有: (1)、减小了冗余的数据传输,节省了网费, (2)、减小了服务器的负担,大大提高了网站的性能, (3)、加快了客户端加载网页的速度javascript
不会向服务器发送请求,直接从缓存中读取资源,在chrome控制台的network选项中能够看到该请求返回200的状态码,而且size显示from disk cache或from memory cache。from memory cache表明使用内存中的缓存,from disk cache则表明使用的是硬盘中的缓存,浏览器读取缓存的顺序为memory –> disk。css
协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程,主要有如下两种状况: •协商缓存生效,返回304和Not Modified 协商缓存生效 •协商缓存失效,返回200和请求结果 协商缓存失效前端
举例来讲, C:S,你几岁了? S:C,我18岁了。 C:S,你几岁了?我猜你18岁了。 S:你知道还问我?(304) C:S,你几岁了?我猜你18岁了。 S:C,我19岁了。(200)java
Respouse Headers缓存相关请求头与说明node
下面经过demo,进行了了验证(因为第一次请求都是须要从服务器端获取数据,返回200,因此就再也不贴验证图了) last-Modifiedwebpack
第二次请求数据未更改时: web
第二次请求数据更改: 算法
客户端第二次请求此URL时,根据HTTP协议的规定,浏览器会向服务器传送If-Modified-Since报头(HttpRequest Header),询问该时间以后文件是否有被修改过:若是服务器端的资源没有变化,则自动返回HTTP304(NotChanged.)状态码,内容为空,这样就节省了传输数据量。当服务器端代码发生改变或者重启服务器时,则从新发出资源,返回和第一次请求时相似。从而保证不向客户端重复发出资源,也保证当服务器有变化时,客户端可以获得最新的资源。chrome
二、ETag工做原理浏览器
第二次发送请求:
(1)数据没有变化
服务端经过对比etag与if-none-match,数据未发生变化时,etag不会发生变化,服务端检测二者相等,返回304
(2) 数据发生变化
数据发生变化时,etag发生变化,服务端检测二者不相等,返回304,返回200
三、 Expires
第二次请求(在有效时间内):
第二次请求(不在有效时间内):
当请求在有效时间类,客户端会直接读取浏览器缓存,返回200(from disk cache),当不在有效时间内时,会进行协商缓存,返回304. 四、 Cache-Control
(在HTTP/1.0中可能部分没实现,仅仅实现了Pragma: no-cache),HTTP/1.1中,Cache-Control是最重要的规则,主要用于控制网页缓存。好比当Cache-Control:max-age=300时,则表明在这个请求正确返回时间(浏览器也会记录下来)的5分钟内再次加载资源,就会命中强缓存。常见有如下六个属性值:
(1) max-age
当max-age=0时,表示浏览器不会进行缓存,以下图所示:
第二次请求:
当max-age = 60,表示浏览器缓存60s
第二次请求(60秒之内):
第二次请求(60秒之外):
由此可看出,在60s之内,浏览器会直接从缓存中获取数据,超过60s,浏览器会从新从服务器端获取数据。
no-cache控制客户端缓存内容,是否使用缓存则须要通过协商缓存来验证决定。咱们具体以实例来理解:
if (req.headers["if-modified-since"] === data) {
res.writeHead(304, "keep cache", {
"Content-Type": "text/plain",
"Cache-Control": "no-cache",
"last-modified": data,
//"Expires": datae
})
res.end();
} else {
res.writeHead(200, "OK", {
"Content-Type": "text/plain",
"Cache-Control": "no-cache",
"last-modified": data,
// "Expires": datae
})
}
复制代码
第二次请求数据无变化:
第二次请求数据发生变化:
当Cache-Control设为no-cache时,须要使用协商缓存判断是否使用缓存,当第二次访问时,将Last-Modified与 if-modified-since进行比较,若相同,则使用协商缓存,返回304,若不一样,则从新访问服务端,返回200。no-cache与Etag和Last-Modified是同样的原理,这里就再也不实验。
no-store即全部内容都不会被缓存,即不使用强制缓存,也不使用协商缓存。
if (req.headers["if-modified-since"] === data) {
res.writeHead(304, "keep cache", {
"Content-Type": "text/plain",
"Cache-Control": "no-store",
"last-modified": data,
//"Expires": datae
"Etag": number
})
res.end();
} else {
res.writeHead(200, "OK", {
"Content-Type": "text/plain",
"Cache-Control": "no-store",
"last-modified": data,
// "Expires": datae
"Etag": number
})
复制代码
第二次请求:
说明,当catch-control设为no-store时,浏览器不会在本地缓存数据,客户端每次访问都会向服务器请求数据。
Expires = 时间,HTTP 1.0 版本,缓存的载止时间,容许客户端在这个时间以前不去检查(发请求) max-age = 秒,HTTP 1.1版本,资源在本地缓存多少秒。 若是max-age和Expires同时存在,则被Cache-Control的max-age覆盖。
Expires 的一个缺点就是,返回的到期时间是服务器端的时间,这样存在一个问题,若是客户端的时间与服务器的时间相差很大,那么偏差就很大,因此在HTTP 1.1版开始,使用Cache-Control: max-age=秒替代。
res.writeHead(200, "keep cache", {
"Content-Type": "text/plain",
"Cache-Control": "max-age=60",
//"last-modified": data,
"Expires": datae
// "Etag": number
})
复制代码
第二次请求(60s以后):
能够看出,虽然Expires的期限未至,但max-age=60s,60s后,浏览器便会从新访问服务器,很明显,若是max-age和Expires同时存在,则被Cache-Control的max-age覆盖。
猜测:当max-age过时后,是否会判断etag或Last-Modified,进行协商缓存了?
复制代码
咱们以事实来讲话:
if (req.headers["if-modified-since"] === data) {
res.writeHead(304, "keep cache", {
"Content-Type": "text/plain",
"Cache-Control": "max-age=60",
"last-modified": data,
//"Expires": datae
// "Etag": number
})
res.end();
} else {
res.writeHead(200, "OK", {
"Content-Type": "text/plain",
"Cache-Control": "max-age=60",
"last-modified": data,
//"Expires": datae
// "Etag": number
})
}
复制代码
第二次请求(60s之内):
第二次请求,数据未发生变化(60s之外):
第二次请求,当数据发生变化时(60s外):
由此咱们能够获得,当max-age的时间过时后,浏览器会进入协商缓存,经过比较last-modify与if-modified-since,若相等,则返回304,从缓存中读取数据,或不相等,返回200,从服务端读取数据。Etag原理与last-modify同样,这里就再也不重复。
五、Cache-Control 与last-modify与Etag
讲到这里,可能咱们会有个疑问,感受etag与last-modify的原理同样,做用同样,为何还会有两个了。咱们就分析下这二者的区别: last-modify的缺点: 一些周期性修改的文件,修改时间变了但内容没变,此时不但愿从新GET; 一些文件修改很是频繁,好比1秒内修改了屡次,Last-Modified只能精确到秒; 一些服务器不能获得文件修改的精确时间; ETag是HTTP/1.1标准开始引入的,它是对Last-Modified的补充。 当etag与last-modify同时存在时,若是同时有 etag 和 last-modified 存在,在发送请求的时候会一次性的发送给服务器,没有优先级,服务器会比较这两个信息(在具体实现上,大多数作法针对这种状况只会比对 etag)。 咱们仍是经过实例来看具体状况:
if (req.headers["if-none-match"] == number) {
if (req.headers["if-modified-since"] === data){
res.writeHead(304, "keep cache", {
"Content-Type": "text/plain",
// "Cache-Control": "no-cache",
"Cache-Control": "max-age=60",
"last-modified": data,
// "Expires": datae,
"Etag": number
})
res.end();
}
} else {
res.writeHead(200, "OK", {
"Content-Type": "text/plain",
"Cache-Control": "no-cache",
"last-modified": data,
//"Expires": datae
"Etag": number
})
}
复制代码
第二次访问数据无变化:
第二次访问last-modify有变化:
第二次访问etag变化:
同时改变就不演示了,其实大多数状况下,当咱们内容改变时,经过相关插件监控,last-modify和etag都会改变,这样就会返回200,从服务器端读取数据。
6 、Cache-Control 与last-modify与Etag与Expires
一般Cache-Control 与 Last-Modified, Etag, Expire 是一块儿混合使用的,特别是 Last-Modified 和 Expire 常常一块儿使用。Last-Modified,Etag,Expires 三个同时使用时。先判断 Expire ,而后发送 Http 请求,服务器同时判断 last-modified和 Etag ,必须都没有过时,才能返回 304 响应。 Cache-Control —— 请求服务器以前 Expires —— 请求服务器以前 If-None-Match (Etag) —— 请求服务器 If-Modified-Since (Last-Modified) —— 请求服务器 咱们一样经过实例来看:
(1)咱们将cache-control设为no-cache,容许使用协商缓存,Expires设为2020年,保证不会到期,而后看下状况:
if (req.headers["if-none-match"] == number) {
if (req.headers["if-modified-since"] === data){
res.writeHead(304, "keep cache", {
"Content-Type": "text/plain",
// "Cache-Control": "no-cache",
"Cache-Control": "max-age=60",
"last-modified": data,
"Expires": datae,
"Etag": number
})
res.end();
}
} else {
res.writeHead(200, "OK", {
"Content-Type": "text/plain",
// "Cache-Control": "no-cache",
"Cache-Control": "max-age=60",
"last-modified": data,
"Expires": datae,
"Etag": number
})
}
复制代码
第二次请求,数据不发生变化:
其实这里,若是只有expires时,应该不会向服务端发起请求,会出现200(from disk cache).当设置了cache-control为no-cache,会覆盖expires,表示须要进入协商缓存这一环节,若检测到数据没变化,则返回304.
(2) 咱们将cache-control设为max-age=60,容许使用协商缓存,Expires设为2020年,保证不会到期,
而后看下状况:
第二次请求(60s之内)
因为还在max-age期限内,因此会直接读取缓存,返回200(from disk cache)
第二次请求(60s之后),数据未变化:
第二次请求(60s之后)当数据发生变化时:
60s之后,因为max-age过时,浏览器会向服务器发送信息,若数据未变化,返回304,若变化返回200和数据,因为请求了服务器,max-age会从新计时,又进入强制缓存。这里能够看出,expires与max-age功能同样,为何还有会两个,其实expires是HTTP1.0中使用的,Expires要求客户端和服务端的时钟严格同步,HTTP1.1中引入Cache-Control来克服Expires头的限制。
讲到这里,其实它们之间的关系已经很明了了,作个总结:
Cache-Control —— 请求服务器以前 Expires —— 请求服务器以前 If-None-Match (Etag) —— 请求服务器 If-Modified-Since (Last-Modified) —— 请求服务器 当cache-control的max-age与Expires时间未过时时,不会向服务器发送请求,使用强缓存。 当cache-control设置为no-cache时,会使用协商缓存。 当cache-control设置为no-store时,不使用缓存,每次都会向服务器发送请求。 Cache-control是克服expires缺陷,HTTP1.1引入,优先级高于expires。 ETag是HTTP/1.1标准开始引入的,它是对Last-Modified的补充。
首先分别对无缓存,协商缓存,强缓存进行9次请求,得出各自耗费的时间。很明显,强缓存所耗时间是最短的。无缓存即每次都须要发送http请求,服务端返回200和数据,协商缓存须要发送一次http请求,服务端进行判断,数据未变返回304,客户端读取浏览器缓存,变化返回200和数据,而强缓存是不发送http请求,客户端直接读取浏览器缓存。因此浏览器缓存与前端性能就主要围绕在是否发送http请求或发送http请求多少来进行优化了。 那么减小http请求有哪些方式了?
CSSSprites直译过来就是CSS精灵,可是这种翻译显然是不够的,其实就是经过将多个图片融合到一副图里面,而后经过CSS的一些技术布局到网页上。特别是图片特别多的网站,若是能用css sprites下降图片数量,带来的将是速度的提高。
由于咱们在浏览器中打开一个页面,页面中所须要用到的每个css,js文件, 客户端都须要发送一次http请求到服务器端获取这些文件,因此在页面的设计时将浏览器一次访问须要的js,css合并成一个文件,这样能够减小http请求来提升网站性能。
对于CSS,Javascript,Logo,图标等这些静态资源文件更新的频率都比较低,因此将这些静态资源文件使用强缓存,这样就能够减小http请求,从而提升性能。页面的初次访问者会进行不少HTTP请求,咱们经过设置Expires头和Cache-Control的max-age,是这些组件可以缓存足够长的时间,这样第二次请求时就直接读取浏览器缓存,从而避免发送http请求,加载速度就会提升不少。 可是经过使用一个长久的Expires头,可使这些组件被缓存,下次访问的时候,就能够减小没必要要的HTPP请求,从而提升加载速度。在某些时候,静态资源文件变换须要更新到客户端的浏览器缓存中,这种状况下能够经过改变文件的文件名来实现,即便用浏览器缓存的网站若是须要更新CSS,JS,Logo等信息,能够经过改变文件名的方式进行更新。当咱们的文件跟新时,咱们最但愿的是咱们更改了哪里,对应的部分进行更新。这里咱们就引入数据摘要要算法对文件求摘要信息,摘要信息与文件内容一一对应,就有了一种能够精确到单个文件粒度的缓存控制依据。目前成熟的解决方案:
如今已经跟新到fis3了,fis3中封装工具方法,咱们调用fis3进行版本发布,就会自动实现上述部署。 咱们只须要在fis-confid.js中配置
fis.match('*.{js,css,png}', {
useHash: true
});
复制代码
这样文件会在打包后的发布版本对应的文件添加惟一的hash值 能够看出,js/css/png等都被加上了md5 hash值,这样当咱们版本中其中一个文件变化,hash才会变,从而保证浏览器能够读取更改的文件。Fis3跟webpack同样是个复杂的打包工具,这只是其中一个小部分。
webpack经过对不一样文件进行hash/chunkhash/contenthash的设置 对png|jpe?g|gif等的设置:
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('media/[name].[hash:7].[ext]')
}
}
复制代码
对js的设置:
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash].js'),
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
},
复制代码
对css的设置:
filename: utils.assetsPath('css/[name].[contenthash].css'),
allChunks: true,
复制代码
当咱们打包时,对应的文件会有惟一标志符:
当有文件更改后,对应的hash值就会变化,浏览器就会向服务器从新请求该对应更改文件。
从HTTP1.1开始,Web客户端能够经过HTTP请求中的Accept-Encoding头来表示对压缩的支持Accept-Encoding: gzip,deflate.若是Web服务器看到请求中有这个头,就会使用客户端列出来的方法中的一种来进行压缩。Web服务器经过响应中的Content-Encoding来通知Web客户端。
下面咱们来看看具体实例
Eg1:腾讯网 对于png等静态资源的设置:
经过对max-age的设置,是浏览器缓存时间为600s,600s以后便会使用协商缓存,这里主要经过last-modify进行比较。
对于css的设置:
对于css,腾讯网首先进行了打包处理,而后只设置了60s的缓存,60s以后再次请求便会进入协商缓存。
对于js:
对于js,一样进行了打包处理,而后设置了60s的缓存,60s以后再次请求便会进入协商缓存。
eg2网易首页: 对于png等静态资源:
网易首页将png等静态资源设置缓存时间为262800s,至关于3天左右的时间。在这期间,再次请求都会使用强缓存。
对于css的设置:
对css的设置,缓存时间达到315360000s,至关于10年,夸张了点。
对于js:
对于js的设置,将max-age设置为3600s,以后便会经过判断etag和last-modify进行协商判断,进入协商缓存。
不过,因为一个网站有很是多的png,js,css,因此对于特别的状况,须要特殊的设置。对于不多变化的静态文件,能够设置很长的缓存时间,例如10年。对于常变化的,就能够不舍缓存时间或很短好比60s。合理设置浏览器缓存将会大大提高网页加载速度,提高前端性能。
浏览器缓存详解:blog.csdn.net/ywh147/arti…
http: baike.baidu.com/item/http/2…
node中文官网:nodejs.org/zh-cn/