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