共计 2629 个字符,预计需要花费 7 分钟才能阅读完成。
浏览器缓存的意义及具体用法
如果不支持浏览器缓存的话,则每次访问、刷新时,网页的所有文件都得重新下载一遍。支持浏览器缓存可以避免这样不必要的性能问题。
如果缓存中有对应的缓存文件,当重新访问、刷新时,会根据缓存的文件是否 最新 来决定是用缓存中的文件还是重新下载一份最新文件。
而要想真正地利用好浏览器缓存,必须得保证每次都做到以下两个步骤:
- 浏览器 每次 拿到结果,都会将请求结果和缓存标识存在浏览器缓存中
-
浏览器 每次 发起请求,都会首先在浏览器缓存中查找结果及缓存标识
- 这是浏览器缓存的核心,他保证了每个请求结果及标识都会被存入缓存中,每次请求都会首先查找缓存中的结果及标识
如果缓存中有对应的缓存文件,我们具体是如何来确定缓存中的文件是否是最新的文件呢?
分两步,先检查强缓存,如若强缓存失效则检查协商缓存。
- 直接检查本地缓存是否最新,不需要和后端沟通【Expires时间戳,Cache-Control时间段】【强缓存有效 200】
- 发起请求,让后端返回该缓存文件是否最新【if-modified-since+last-modified修改时间,、If-None-Match+ETag文件指纹 hash】。如果是最新【304】,后端不返回文件,则从缓存中拿文件。如果不是最新【200】,后端返回文件。
强缓存存在的问题
当强缓存确定为失效后,才检查协商缓存。但是强缓存失效不一定就说明该文件的缓存不是最新的了,还得由协商缓存来判断。但是强缓存如果是有效的话,就不检查协商缓存了,而是直接判断该文件是最新,直接复用该文件。
但这也导致了一个问题:强缓存判断为最新仅是根据是否超出每个时间点或时间段,而并不关心服务器文件是否更新过了一次。也就是说,有可能即使强缓存判断该文件为最新,实际上该文件在服务器中已经更新了一次甚至更多。
为了解决这个问题,我们提出了一套解决方案
- 对于频繁更新的文件,我们在响应头的 cache-control 里设置时间非常短,如 1800,甚至是 no-cache 不要存在缓存中。【如网页 html 文件,我们都是将他的 cache-control 时间设置的非常短。因为 html 文件中引用静态文件的代码往往会频繁更新,在后面讲到文件指纹时,大家就能理解了】
- 对于几乎不更新的文件,我们在响应头的 cache-control 里设置时间非常长,一个月甚至一年
新的问题
但这样还是不够的,因为 我们并不能如此武断地肯定在我们设置的 cache-control 时间段内服务器上的文件一定不会更新,有可能说我以为该文件一个月之内不会被迭代更新,然后 cache-control 的值设置为一个月,但十天后该文件就是被修改了,这样的话,当用户请求该文件时,浏览器发现缓存中有该文件的缓存文件,并且强缓存有效,则直接使用该文件,从而导致浏览器中使用的文件与服务器最新文件不符
但是我们又不可能将所有文件的 cache-control 都设置地极其短来保证每次请求到的都是最新的文件,如果这样的话,浏览器缓存的存在还有什么意义?
所以说,cache-control 的值只能设置成大概,但他并不是可靠的。
所以在这里我们引入了一个新的工具 —-文件指纹【将文件内容 hash 后的值作为文件名的后部分,以保证一旦文件内容不同,则 hash 后的值不同】
具体的 hash 算法大家可以了解一下,由于其不同值 hash 后的值一定不同的特性【值得注意的是,hash 算法并不是严格的不同值 hash 后的值一定不同,但由于该碰撞的几率极低,所以一般不做考虑】,我们一般将 hash 算法用于身份证明 —-如若两个 hash 后的值相同,则证明这两个对应的 hash 前的值一定相同
一旦静态文件用了文件指纹后,我们可以将其强缓存的时间设置的非常长,一年都可以。不过要注意的是,html 文件的强缓存时间一定要设置的非常短,以保证每次访问 html 文件都是服务器最新的。这样的话,html 里的文件所引用的静态文件名一直都是最新的了
之所以文件指纹能做到这样的效果是因为当静态文件更新了,该文件名的文件指纹部分更新,导致 html 文件引用的静态文件名的文件指纹部分也更新了。这样的话,浏览器在缓存中找不到相同文件名的缓存文件,从而直接跳过强缓存、协商缓存这两步,直接去服务器访问下载该文件
在每次请求静态文件的 url 后面加上个随机数的 query 也可以解决该问题,在这里就不展开讨论了
浏览器机制流程图
注意,每次引用静态文件时,都会查看一下缓存中是否有该文件的缓存文件,即使是第一次访问该网页,没准缓存中存有该网页引用到的静态文件呢【如两个网站引用了同一个地址的图片】
也就只有首次访问该网页或请求该文件时的请求头 cache-control:no-cache,才不会有强缓存 + 协商缓存的步骤。
请求头与响应头中的 cache-control:no-cache 的差别
请求头的 no-cache 表示不从缓存中拿,响应头的 no-cache 表示该文件不要存在缓存中。
响应头由后端设置,我们管不着。但请求头我们是可以设置,但由于 js、css 等静态文件不是通过 ajax 来获取到的,所以当访问这些静态文件时我们没法在 ajax 里设置 header 来设置请求头,但是我们可以在 meta 里设置 header 来设置请求头从而控制是否从浏览器缓存中拿对应的缓存文件。
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
// 这意味着所有静态文件都不从缓存中拿,不管后端响应头有没有设置将文件存于浏览器缓存中。这个对性能有很大影响,一般别用
浏览器三种刷新机制
- 首次访问该网页:所有文件都无对应的缓存文件,都需从服务器下载,然后根据服务器的 cache-control 来决定是否将该文件存入浏览器缓存中。【没有检查强缓存及协商缓存的步骤】
- 普通刷新:大部分文件都有对应的缓存文件,请求文件的第一步就是检查强缓存 + 协商缓存,从而决定是从缓存中拿文件还是从服务器下载文件。【除了 cache-control:no-cache 的文件,如 json 文件】
- 强制刷新:大部分文件都有对应的缓存文件,所有文件的请求的请求头都设置为 cache-control:no-cache,从而都没有强缓存 + 协商缓存,而是直接一律下载
- 清空缓存并强制刷新:将浏览器缓存都清空,也就使得所有文件都无对应的缓存文件,而后操作和强制刷新一样