看了这篇文章,浏览器缓存必定能记住

关于浏览器缓存方面的知识,也看了好几篇文章了。你们提到也总能说几个关键词出来,可是说到如何使用可能又不够肯定了。所以我在这经过实操记录下来,方便更好的理解和记忆。前端

首先,常说的浏览器缓存的两种状况强缓存协商缓存,优先级较高的是强缓存,当强缓存命中失败时才会走协商缓存。git

强缓存

强缓存是不须要发送http请求的,当资源命中强缓存时,直接从缓存中获取,响应状态返回200,打开控制台查看Size也不显示资源大小,而是告诉咱们来自缓存。github

强缓存

强缓存

强缓存如何实现呢,本文后端代码都经过Koa来演示web

一、Expires

设置过时时间,第一次请求之后响应头中设置Expires后端

router.get('/img/1.jpg', ctx => {
    const file = fs.readFileSync(path.resolve(__dirname,`.${ctx.request.path}`))
    ctx.set({
        'Expires': new Date((+new Date() + 1000*60*60*24*3))
    })
    ctx.body = file
})
复制代码

将过时时间设置为三天后,这样在缓存未过时下就不须要再从新请求服务端了。可是Expires存在问题,就是客户端的时间和服务端时间多是不一致的,好比你把电脑时间设置成三天甚至一年后,缓存就无效了。浏览器

二、Cache-Control

HTTP1.1新增了Cache-Control字段,Cache-Control优先级高于Expires,二者同时使用时会忽略Expires(Expires保留的做用是向下兼容),Cache-Control字段属性值比较灵活。缓存

2.1 max-age

max-age指定资源的有效时间,单位是秒。如下是demo2,Cache-controla:max-age=0,同时设置Expires。测试能够看到图片始终不会缓存,也体现出了优先级的问题。服务器

// demo2
router.get('/img/2.jpg', ctx => {
    const file = fs.readFileSync(path.resolve(__dirname,`.${ctx.request.path}`))
    ctx.set({
        'Expires': new Date((+new Date() + 1000*60*60*24*3)),
        'Cache-control': 'max-age=0'
    })
    ctx.body = file
})
复制代码

再测试demo3,这样图片的缓存时间就是10秒koa

// demo3
router.get('/img/3.jpg', ctx => {
    const file = fs.readFileSync(path.resolve(__dirname,`.${ctx.request.path}`))
    ctx.set({
        'Cache-control': 'max-age=10'
    })
    ctx.body = file
})
复制代码

2.2 s-maxage

s-maxagemax-age相似,不一样的地方s-maxage是针对代理服务器的测试

// demo4
router.get('/img/4.jpg', ctx => {
    const file = fs.readFileSync(path.resolve(__dirname,`.${ctx.request.path}`))
    ctx.set({
        'Expires': new Date((+new Date() - 1000*60*60*24*3)),
        'Cache-control': 's-maxage=10'
    })
    ctx.body = file
})
复制代码

测试发现图片没法缓存

2.3 private和public

和前面两个属性相关,private对应资源能够被浏览器缓存,public表示资源既能够被浏览器缓存,也能够被代理服务器缓存。 默认值是private,至关于设置了max-age的状况;当设置了s-maxage属性,就表示能够被代理服务器缓存,也就是等同于设置成public

这里请看demo5

// demo5
router.get('/img/5.jpg', ctx => {
    const file = fs.readFileSync(path.resolve(__dirname,`.${ctx.request.path}`))
    ctx.set({
        'Cache-control': 'private'
    })
    ctx.body = file
})
复制代码

测试发现图片始终不会缓存,因此private对应max-age的默认值应该是0。可是private真的和max-age=0彻底相同吗,我又写了个栗子测试。

//demo6
router.get('/img/6.jpg', ctx => {
    const file = fs.readFileSync(path.resolve(__dirname,`.${ctx.request.path}`))
    ctx.set({
        'Expires': new Date((+new Date() + 1000*60*60*24*3)),
        'Cache-control': 'private'
    })
    ctx.body = file
})
复制代码

测试发现图片会被缓存,说明private状况下,不会让Expires失效。

2.4 no-store和no-cache

no-store比较暴力,不适用任何缓存机制,直接向服务器发起请求,下载完整资源。

no-cache跳过强缓存,也就是Expiresmax-age等都无效了,直接请求服务器,确认资源是否过时,也就是进入协商缓存的阶段。协商缓存中有两个关键字段,Last-ModifiedEtag

协商缓存

一、Last-Modified和If-Modified-Since

请看demo8

// demo8
router.get('/img/8.jpg', ctx => {
    const file = fs.readFileSync(path.resolve(__dirname,`.${ctx.request.path}`))
    const stats = fs.statSync(path.resolve(__dirname,`.${ctx.request.path}`))
    if(ctx.request.header['if-modified-since'] === stats.mtime.toUTCString()){
        return ctx.status = 304
    }
    ctx.set({
        'Cache-control': 'no-cache',
        'Last-Modified': stats.mtime.toUTCString()
    })
    ctx.body = file
})
复制代码

第一次请求后,在响应头中加入Last-Modified字段,返回资源的最后修改时间,下次请求时客户端请求头会带上If-Modified-Since的字段,里面的值就是以前响应头中Last-Modified的值,而后进行比较,若是没有变化,则返回304的状态码。

协商缓存

若是改造一下把'Cache-control': 'no-cache'去掉呢,测试后发现,在不清缓存的状况资源就变成强缓存了,请求头中的If-Modified-Since也没有了(加上也没用,由于不会发送http请求),致使文件更新就没法检测了。

二、 ETag和If-None-Match

请看demo9

// demo9
router.get('/img/9.jpg', ctx => {
    const file = fs.readFileSync(path.resolve(__dirname,`.${ctx.request.path}`))
    const stats = fs.statSync(path.resolve(__dirname,`.${ctx.request.path}`))
    if(ctx.request.header['if-none-match']){
        if(ctx.request.header['if-none-match'] === 'abc123'){
            return ctx.status = 304
        }
    }else if(ctx.request.header['if-modified-since'] === stats.mtime.toUTCString()){
        return ctx.status = 304
    }
    ctx.set({
        'Cache-control': 'no-cache',
        'Last-Modified': stats.mtime.toUTCString(),
        'ETag': 'abc123'
    })
    ctx.body = file
})
复制代码

Last-Modified相似,第一次请求之后,响应头中会增长ETag字段,ETag经过资源的内容生成一个标识符,下次请求在请求中增长If-None-Match字段,值就是以前响应头中ETag的值,而后进行比较。ETag能够说是对Last-Modified的一个补充,由于Last-Modified也是有不足的地方。举个栗子,Last-Modified中的时间是精确到秒的,若是同一秒内文件被修改了一次,下一次请求时,预期获取新资源而实际仍是会走协商缓存。而ETag是基于资源内容的,因此会生成新的值,所以能达到预期效果。

补充:浏览器缓存的四个位置

  1. Service Worker Cache
  2. Memory Cache
  3. Disk Cache
  4. Push Cache

Service Worker Cache,这个不少人都听过,是PWA应用的重要实现机制,推荐资源:PWA应用实战

Memory CacheDisk Cache就是前面总结的,咱们强缓存和协商缓存存放资源的位置。Memory Cache内存缓存,是效率最高的,固然内存资源也是昂贵的有限的,不可能都使用内存缓存,Disk Cache磁盘缓存,相对来讲读取速度慢些。通常来讲,大文件或者内存使用高的状况下,资源会被丢进磁盘。

Push Cache推送缓存,缓存的最后一道防线。是HTTP2中的内容,须要自行去了解。

总结几个点

一、协商缓存中有两组约定的字段,一是Last-ModifiedIf-Modified-Since;二是ETagIf-None-Match。也就是响应头中存在Last-Modified(或ETag),则下次请求的请求头中会自动增长If-Modified-Since(或If-None-Match)字段,至因而否走协商缓存取决于具体代码,好比触发条件通常就是比较同一组数据是否相同,同时由于第二组更加准确,因此优先级也更高。

二、Cache-Control: privateCache-Control: max-age=0,效果不彻底相同,前者不会让Expires失效。

三、浏览器缓存机制总览,首先若是命中强缓存就直接使用;不然就进入协商缓存阶段,这里会产生http请求,经过协商缓存的两组规范,检查资源是否更新。没更新的话就返回304状态码,不然就从新获取资源并返回200状态码。

四、经过上面的演示能够看得出来,处理缓存的工做量主要在后端,而后在工做中,静态资源咱们通常都是直接使用中间件来处理,好比笔者在Koa项目中的话,会用koa-static这个中间件。前端不须要写相关代码,后端用现成的轮子,所以缓存相关的知识就被抛弃了。

五、看完这些demo,相信你必定能记住,demo地址,也能够本身clone下来跑几遍试试,但愿本文对你有帮助。

相关文章
相关标签/搜索