Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

浏览器缓存知识总结 #4

Open
tinet-jutt opened this issue Apr 20, 2021 · 0 comments
Open

浏览器缓存知识总结 #4

tinet-jutt opened this issue Apr 20, 2021 · 0 comments

Comments

@tinet-jutt
Copy link
Owner

tinet-jutt commented Apr 20, 2021

0. 前言

浏览器缓存作为性能优化的重要一环,对于前端而言,重要性不言而喻。之前被人问起浏览器缓存的知识,感觉自己有点一知半解,所以这次好好整理总结了一下。

1. 浏览器缓存分类

目前主流的浏览器缓存分为两类,强缓存和协商缓存,它们的匹配流程如下:

(1)浏览器发送请求前,根据请求头的expires和cache-control判断是否命中强缓存策略,如果命中,直接从缓存获取资源,并不会发送请求。如果没有命中,则进入下一步。

(2)没有命中强缓存规则,浏览器会发送请求,根据请求头的last-modified和etag判断是否命中协商缓存,如果命中,直接从缓存获取资源。如果没有命中,则进入下一步。

(3)如果前两步都没有命中,则直接从服务端获取资源。

2. 强缓存

2.1 强缓存原理

强缓存需要服务端设置expirescache-control

nginx代码参考,设置了一年的缓存时间:

location ~ .*\.(ico|svg|ttf|eot|woff)(.*) {
  proxy_cache               pnc;
  proxy_cache_valid         200 304 1y;
  proxy_cache_valid         any 1m;
  proxy_cache_lock          on;
  proxy_cache_lock_timeout  5s;
  proxy_cache_use_stale     updating error timeout invalid_header http_500 http_502;
  expires                   1y;
}

(1)expires:从图可以看出,expires的值是一个绝对时间,是http1.0的功能。如果浏览器的时间没有超过这个expires的时间,代表缓存还有效,命中强缓存,直接从缓存读取资源。不过由于存在浏览器和服务端时间可能出现较大误差,所以在之后http1.1提出了cache-control。

(2)cache-control:从图可以看出,cache-control的值是类似于max-age=31536000这样的,是一个相对时间,31536000是秒数,正好是一年的时间。当浏览器第一次请求资源的时候,会把response header的内容缓存下来。之后的请求会先从缓存检查该response header,通过第一次请求的date和cache-control计算出缓存有效时间。如果浏览器的时间没有超过这个缓存有效的时间,代表缓存还有效,命中强缓存,直接从缓存读取资源。

两者可以同时设置,但是优先级cache-control > expires。

2.2 from disk cache 和 from memory cache

透过现象看本质
(1)访问 https://tomatoKnight.github.io/ –> 200 –> 关闭博客的标签页
(2)重新打开 https://tomatoKnight.github.io/ –> 200(from disk cache)
(3)刷新 –> 200(from memory cache)

过程如下:

(1)访问 https://tomatoKnight.github.io/

(2)关闭博客的标签页重新打开 https://tomatoKnight.github.io/

(3)刷新

得出总结:

  • 浏览器打开一个新网页首次资源都是从网络请求获得,拿到资源后根据响应头设置缓存规则;
  • 浏览器缓存会把网络请求的资源缓存进内存(memory)和硬盘(disk);
  • 关闭网页然后重新打开时资源会从硬盘读取出来,同时设置内存缓存;
  • 刷新页面则直接加载内存中的缓存;

看到这里可能有人小伙伴问了,最后一个步骤刷新的时候,不是同时存在着from disk cache和from memory cache吗?

对于这个问题,我们需要了解内存缓存(from memory cache)和硬盘缓存(from disk cache),如下:

内存缓存(from memory cache):内存缓存具有两个特点,分别是快速读取和时效性:

  • 快速读取:内存缓存会将编译解析后的文件,直接存入该进程的内存中,占据该进程一定的内存资源,以方便下次运行使用时的快速读取。

  • 时效性:一旦该进程关闭,则该进程的内存则会清空。

硬盘缓存(from disk cache):硬盘缓存则是直接将缓存写入硬盘文件中,读取缓存需要对该缓存存放的硬盘文件进行I/O操作,然后重新解析该缓存内容,读取复杂,速度比内存缓存慢。

在浏览器中,浏览器会在js和图片等文件解析执行后直接存入内存缓存中,那么当刷新页面时只需直接从内存缓存中读取(from memory cache);而css文件则会存入硬盘文件中,所以每次渲染页面都需要从硬盘读取缓存(from disk cache)。

2.3 强缓存作用
强缓存作为性能优化中缓存方面最有效的手段,能够极大的提升性能。由于强缓存不会向服务端发送请求,对服务端的压力也是大大减小。

对于不太经常变更的资源,可以设置一个超长时间的缓存时间,比如一年。浏览器在首次加载后,都会从缓存中读取。

但是由于不会向服务端发送请求,那么如果资源有更改的时候,怎么让浏览器知道呢?现在常用的解决方法是加一个?v=xxx的后缀,在更新静态资源版本的时候,更新这个v的值,这样相当于向服务端发起一个新的请求,从而达到更新静态资源的目的。

3. 协商缓存

3.1 协商缓存原理
在强缓存没有命中的时候,就是协商缓存发挥的地盘了。协商缓存会根据[last-modified/if-modified-since]或者[etag/if-none-match]来进行判断缓存是否过期。

nginx代码参考:

location ~ .*\.(ico|svg|ttf|eot|woff)(.*) {
  proxy_cache               pnc;
  proxy_cache_valid         200 304 1y;
  proxy_cache_valid         any 1m;
  proxy_cache_lock          on;
  proxy_cache_lock_timeout  5s;
  proxy_cache_use_stale     updating error timeout invalid_header http_500 http_502;
  etag                                       on;
}


(1)last-modified/if-modified-since: 浏览器首先发送一个请求,让服务端在response header中返回请求的资源上次更新时间,就是last-modified,浏览器会缓存下这个时间。然后浏览器再下次请求中,request header中带上if-modified-since:[保存的last-modified的值]。根据浏览器发送的修改时间和服务端的修改时间进行比对,一致的话代表资源没有改变,服务端返回正文为空的响应,让浏览器中缓存中读取资源,这就大大减小了请求的消耗。由于last-modified依赖的是保存的绝对时间,还是会出现误差的情况:一是保存的时间是以秒为单位的,1秒内多次修改是无法捕捉到的;二是各机器读取到的时间不一致,就有出现误差的可能性。为了改善这个问题,提出了使用etag。

(2)etag/if-none-match:

etag是http协议提供的若干机制中的一种Web缓存验证机制,并且允许客户端进行缓存协商。生成etag常用的方法包括对资源内容使用抗碰撞散列函数,使用最近修改的时间戳的哈希值,甚至只是一个版本号。 和last-modified一样,浏览器会先发送一个请求得到etag的值,然后再下一次请求在request header中带上if-none-match:[保存的etag的值]。通过发送的etag的值和服务端重新生成的etag的值进行比对,如果一致代表资源没有改>变,服务端返回正文为空的响应,告诉浏览器从缓存中读取资源。

etag能够解决last-modified的一些缺点,但是etag每次服务端生成都需要进行读写操作,而last-modified只需要读取操作,从这方面来看,etag的消耗是更大的。

3.2 协商缓存作用
协商缓存是无法减少请求数的开销的,但是可以减少返回的正文大小。一般来说,对于勤改动的html文件,使用协商缓存是一种不错的选择。

4. 刷新缓存方法

刷新强缓存可以使用?v=xxx的后缀。当然,人工更改版本号的成本比较高,而且难以维护,现在主流的是通过webpack等打包工具生成[name].[hash].js之类的文件名,也能刷新强缓存。

刷新协商缓存比较简单,修改文件内容即可。

对于浏览器而言,在Chrome中,你可以使用审查元素,高版本也叫检查,将Network中的Disable cache打勾,使用cmd+r刷新页面即可。当然你也可以使用强制刷新,直接在页面使用cmd+shift+r进行刷新。

5. 结尾

以上就是对浏览器缓存的一点拙见,欢迎一起交流。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant