阅读 5281

前端缓存那些事

前端缓存指的是,浏览器对服务器最近请求过的资源进行存储,通过这种方式来达到减少与服务器的交互请求,以此减少对带宽流量的浪费,以及减少了服务器的负担,而浏览器缓存主要分为两种,强缓存和协商缓存

1.强缓存

强缓存所谓的“强”,在于强制让浏览器按照一定时间范围内来存储来自服务器的资源,有点强制的味道~,强缓存是利用Expires或者Cache-Control,不发送请求,直接从缓存中取,请求状态码会返回200(from cache)

1.1 Expires(已逐步淘汰)

Expires是HTTP/1.0中提及的,让服务器为文件资源设置一个过期时间,在多长时间内可以将这些内容视为最新的,允许客户端在这个时间之前不去检查,MDN 具体介绍 点此

  • 指定到期时间

指定缓存到期GMT的绝对时间,如果expires到期需要重新请求

Expires:Sat, 09 Jun 2020 08:13:56 GMT
复制代码

1.2 Cache-Control(主要)

相比上一小节讲的 expires,两者有什么区别呢? Cache-Control 你可以理解成为高级版expires,为了弥补Expires的缺陷在Http1.1协议引入的,且强大之外优先级也更高,也就是当Expires和Cache-Control同时存在时,Cache-Control 会覆盖Expires的配置,即Cache-Control ( Http 1.1 ) > Expires ( Http 1.0 )

Cache-Control 比Expires比较要内涵,具备更多的属性,其中包括如下

• no-cache :可以在本地缓存,可以在代理服务器缓存,需要先验证才可使用缓存

• no-store : 禁止浏览器缓存,只能通过服务器获取

• max-age : 设置资源的过期时间(效果与expires一样)

例子演示:

// 设置缓存时间为1年
Cache-Control: max-age=31536000
Expires:Sat, 09 Jun 2020 08:13:56 GMT //同时设置两个,Expires会失效
复制代码

则意味着浏览器可以缓存一年的时间,无需请求服务器,同时如果同时声明Expires和Cache-Control,Expires将失效

🤔️你可能会有疑惑Cache-Control no-cache与max-age=0有什么区别?

本质上就是你按浏览器刷新与强制刷新的区分,看下一节

1.3 用户对浏览器的操作

相信你离不开的操作就是F5(刷新按钮),但是不同的刷新操作意味着不同的反应

• Ctrl + F5 (强制刷新)::request header多了cache-control: no-cache (重新获取请求)

• F5 (刷新)/ctrl+R刷新::request header多了 cache-control: max-age=0 (需要先验证才可使用缓存,Expires无效)

2.协商缓存

协商缓存,就没有强缓存那么霸道,协商缓存需要客户端和服务端两端进行交互,通过服务器告知浏览器缓存是否可用,并增加缓存标识,“有事好好商量”,两者都会互相协商。 协商缓存,其实就是服务器与浏览器交互过程,一般有两个回合,而协商主要有以下几种方式:

2.1 Last-Modified (Http 1.0)

• 第一回合:当浏览器第一次请求服务器资源时,服务器通过Last-Modified 来设置响应头的缓存标识,把资源最后修改的时间作为值写入,再将资源返回给浏览器

• 第二回合:第二次请求时,浏览器会带上 If-Modified-Since 请求头去访问服务器,服务器将 If-Modified-Since 中携带的时间与资源修改的时间对比,当时间不一致时,意味更新了,服务器会返回新资源并更新Last-Modified,当时间一致时,意味着资源没有更新,服务器会返回304状态码,浏览器将从缓存中读取资源

//response header 第一回合
Last-Modified: Wed, 21 Oct 2019 07:28:00 GMT

//request header 第二回合
If-Modified-Since: Wed, 21 Oct 2019 07:29:00 GMT 
复制代码

2.2 Etag (Http 1.1)

MDN中提到ETag 之间的比较,使用的是强比较算法,即只有在每一个字节都相同的情况下,才可以认为两个文件是相同的,而这个hash值,是由对文件的索引节、大小和最后修改时间进行Hash后得到的,而且要注意的是分布式系统不适用,了解更多点我

• 第一回合: 也是跟上文一样,浏览器去请求服务器资源,不过这次不是通过Last-Modified了,而是用Etag来设置响应头缓存标识。Etag是由服务端生成的,然后浏览器会将Etag与资源缓存

• 第二回合: 浏览器会将 Etag 放入 If-None-Match 请求头中去访问服务器,服务器收到后,会对比两端的标识,当两者不一致时,意味着资源更新,会从服务器的响应读取资源并更新Etag,浏览器将从缓存中读取资源,当两者一致时,意味着资源没有更新,服务器会返回304状态码,浏览器将从缓存中读取资源

//response header 第一回合
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"

//request header 第二回合
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
复制代码

对比完 Last-Modified 与 Etag,我们可以很显然看到,协商缓存每次请求都会与服务器发生“关系”,第一回合都是拿数据和标识,而第二回合就是浏览器“咨询”服务器是否资源已经更新的过程。

同时,如果以上两种方式同时使用,Etag 优先级会更高,即 Etag( Http 1.1 ) > Last-Modified ( Http 1.0 )

3.缓存状态码

3.1 状态码200 OK(from cache)

这是浏览器没有跟服务器确认,直接用了浏览器缓存,性能最好的,没有网络请求,那么什么情况会出现这种情况?一般在expires或者 Cache-Control 中的max-age头部有效时会发生

3.2 状态码304 Not Modified

是浏览器和服务器“交流”了,确定使用缓存后,再用缓存,也就是第二节讲的通过Etag或Last-Modified的第二回合中对比,对比两者一致,则意味资源不更新,则服务器返回304状态码

3.3 状态码 200

以上两种缓存全都失败,也就是未缓存或者缓存未过期,需要浏览器去获取最新的资源,效率最低 一句话:缓存是否过期用:Cache-Control(max-age), Expires,缓存是否有效用:Last-Modified,Etag

4.缓存的应用

讲述缓存在我们开发中最常见的使用

4.1 Vue中缓存的应用

• keepAlive

vue官方文档提到,当在这些组件之间切换的时候,你有时会想保持这些组件的状态,以避免反复重渲染导致的性能问题,这个时候我们希望那些标签的组件实例能够被在它们第一次被创建的时候缓存下来,我们可以用一个 元素将其动态组件包裹起来 官方文档🚀

主要用于保留组件状态或避免重新渲染,也意味着不会再走mounted,beforeDestroy函数,组件将被缓存,不用销毁重新渲染,性能比较好

路由的选择性缓存

// router.js
export default new Router({
routes:[
   { path: '/test',
    name: 'test',
    component: () => import('@/views/test/test.vue'),
    meta: {
      title: '测试',
      keepAlive: true
    }
  },
  
 // App.vue
 <keep-alive v-if='$route.meta.keepAlive'>
   <router-view></router-view>
</keep-alive>
 <router-view v-if='!$route.meta.keepAlive'></router-view>
复制代码

组件缓存

 <keep-alive>
  <component v-bind:is="currentTabComponent"></component>
</keep-alive>
复制代码
  • 打包加入hash

前端工程化开发,可以使用 Webpack 编译,打包的资源文件路径里自动带有一串随机字符串,称为 hash

在vue cli脚手架中,我们可以通过配置vue.config.js(本质上是配置webpack)来设置编译生成的文件具备hash值,意味着每次打包编译的文件都是唯一的,来防止因为缓存,导致资源没有更新,官方文档🚀

Vue-Cli 3x版本

// vue.config.js
module.exports = { 
  filenameHashing: true,
  chainWebpack: (config) => {
      config.output.filename('[name].[hash].js').end();
  }
}
复制代码

4.2 Nginx的缓存

• 配置expires

假设我想通过web应用的图片缓存一周,那你可以在nginx中配置如下👇,配置完之后一周之内的资源只会访问浏览器的资源,而不是去请求Nginx

 location ~ \.(gif|jpg|jpeg|png)$ {
          root  /var/mywww/html/public/
          expires 7d;  //表示把数据缓存7天,d:天,s:秒,m:分
  }
复制代码
  • 设置 etag
 location ~ \.(gif|jpg|jpeg|png)$ {
          root  /var/mywww/html/public/
          etag  off;  // 默认是开启 on
  }
复制代码

更多的Nginx学习,可以看我上一篇 《Nginx的那些事》

欢迎指出问题~

请你喝杯🍵 记得三连哦~

1.阅读完记得给🌲 酱点个赞哦,有👍 有动力

2.关注公众号前端那些趣事,陪你聊聊前端的趣事

3.文章收录在Github frontendThings 感谢Star✨

本文使用 mdnice 排版