前端缓存

分为两种:

  1. 浏览器缓存:cookie、localStore 等,存储少量信息

  2. HTTP缓存:存储文件副本

一、浏览器缓存

存储少量信息

  • cookie

  • local store

  • sessionStorage

二、HTTP缓存

针对静态资源的缓存,遵循 HTTP 缓存规则,通过响应头进行缓存。

可分为两种缓存:

  • 强制缓存

  • 协商缓存

浏览器中,Ajax 的缓存和 HTTP 的缓存基本是一样的,根据响应头自动进行处理。

强制缓存

客户端自己判断是否读取缓存,不向服务端发送请求

基于 Expires 实现(废弃)

expire:期满,失效

响应时,给文件设置一个失效期。

1
Expires: Tue, 03 Jul 2001 06:00:00 GMT

问题:过度依赖本地时间,如果本地时间与服务器不同步,就会出现资源无法被缓存或者资源永远被缓存的情况。

基于 Cache-control 实现

代替 Expires 方案,使用时长代替时间点。

Cache-control 的可用属性:

  • max-age:浏览器缓存时长

  • s-maxage:代理服务器缓存时长

  • no-cache:不使用“强制缓存”,使用“协商缓存”

  • no-store:不使用任何缓存

  • private:私有缓存,资源不可以被代理服务器缓存,只能被浏览器缓存

  • public:公开缓存,资源可以被代理服务器缓存

s-maxage 属性必须和 public 属性一起使用,决定代理服务器的缓存

在一般的项目架构中 max-age 就够用

协商缓存

给服务端发送请求,让服务端判断是否读取缓存。

返回 HTTP 304,则表示缓存可用。

基于 last-modified 实现

(1)第一次访问资源时,服务端读取文件的修改时间,给该资源的响应头赋值

  • last-modified 字段赋值为文件修改时间

  • Cache-Control 字段值设置为 no-cache,跳过强制缓存

(2)当客户端接收到响应时,会记录该资源的修改时间,下一次访问时,赋给请求头的 If-Modified-Since 属性

(3)服务端接收到请求时,读取请求头的修改时间和对应文件的修改时间进行比较,如果相等则返回 304,让客户端去读缓存,如果不相等则返回新的资源

存在的问题:

  • 在文件内容本身不修改的情况下,依然有可能更新文件修改时间,导致缓存失效

  • 文件修改时间记录的最小单位是秒,如果文件在极短时间内完成了修改(毫秒级),文件修改时间不会改变,导致客户端不能及时更新

基于 ETag 实现

ETag 就是将原先比较时间戳的形式改成了比较文件指纹

文件指纹:根据文件内容计算出的唯一哈希值

(1)第一次访问资源时,服务端读取文件并计算出文件 hash 值,赋给响应头的 etag 属性

(2)客户端之后访问该资源时,让请求头的 if-none-match 属性携带该文件的 hash 值

(3)服务端读取请求头的 if-none-match 属性,然后计算度对应文件的hash值进行比对,如果相等,说明文件没有修改,返回 304 让客户端去读取缓存,否则返回新的文件

Etag 的缺点:计算量大,影响服务器性能。

如何设置缓存

缓存是缓存在前端,但实际上代码是后端开发来写的,加相关响应头及其对应的校验逻辑即可。

浏览器可以自动判断是否存在缓存,并根据响应头进行相应的缓存处理。

缓存方式的选择

(1)有哈希值的文件设置为强制缓存,如 Vue 打包好的 css、js 文件,每次打包文件名会发生变换,每次更新后,客户端就会检测不到对应文件,从而重新去获取资源

(2)没有哈希值的文件设置为协商缓存,如 index.html,让服务端检测