1. 浏览器缓存
浏览器缓存区分为强缓存,以及协商缓存。
1.1 强缓存
浏览器在满足一定条件时,不发送网络请求,直接使用缓存数据。
1.1.1 expires
expires 是 http1.0 的字段。首次请求时服务端会在 http 响应头上加上 expires 字段,并设置一个过期时间。如 Expires: Thu, 10 Sep 2020 09:30:06 GMT
。在这个时间之前我们去请求资源,浏览器就不会发起新的请求,直接使⽤本地已经缓存好的资源。
expires 是最开始的强缓存方案。存在的问题是,⽤本地时间和 expires 设置的时间进⾏⽐较。如果服务端的时间和我们本地的时间存在误差,那么缓存这个时候很容易就失去了效果。
1.1.2 Cache-Control
Cache-Control 是 http1.1 的字段,其功能更为强大。Cache-Control 设置的是⼀个相对时间,可以更加精准地控制资源缓存,如 Cache-Control: max-age=315360000
,单位是秒。解决了服务端和本地时间不统⼀造成的缓存问题。
初次之外,Cache-Control 还有其他的字段:
public:资源可以被任何对象(包括:发送请求的客户端、代理服务器等等)缓存;
private:资源只能被⽤户浏览器缓存,不允许任何代理服务器缓存;
no-cache:先和服务端确认返回的资源是否发⽣了变化,如果资源未发⽣变化,则直接使⽤缓存好的资源;
no-store:禁⽌任何缓存,每次都会向服务端发起新的请求,拉取最新的资源;
max-age:设置缓存的最⼤有效期,单位为秒;
s-maxage:适用于CDN等代理缓存,于 max-age 作用一致,优先级大于 max-age;
max-stale:在设置的时间内,即使缓存过期,也是用该缓存;
min-fresh:在设置的时间内,请求最新的资源;
Cache-Control 的优先级大于 expires。
1.2 协商缓存
协商缓存即当强缓存失效时,浏览器请求服务器,判断是否使用缓存的过程。当强缓存失效时,浏览器携带缓存标识向服务器发起请求,如果资源无更新,则服务器返回304,浏览器依旧使用缓存;如果资源更新,则服务器返回200,同时返回新的资源。
协商缓存同样可通过两种 http 头部字段实现。
1.2.1 Last-Modified 和 If-Modified-Since
浏览器在第一次访问资源时,服务器返回资源的同时,在响应头中添加 Last-Modified 字段,其值是这个资源在服务器上的最后修改时间。
浏览器下一次请求这个资源时,就会携带 If-Modified-Since 这个字段,其值就是 Last-Modified 中的值。
服务器再次收到这个资源请求,会根据 If-Modified-Since 中的值与服务器中这个资源的最后修改时间对比。如果没有变化,则返回304,浏览器直接从缓存读取资源;如果 If-Modified-Since 的时间小于服务器中这个资源的最后修改时间,说明文件有更新,则返回200和新的资源文件。
其缺点在于
服务端对 Last-Modified 标注的最后修改时间只能精确到秒级,如果某些⽂件在1秒钟以内被修改多次的话,这个时候服务端⽆法准确标注⽂件的修改时间。
服务端上某些操作(打开但不编辑或重新生成)导致文件内容没有修改,但更新了 Last-Modified ,导致缓存失效。
1.2.2 ETag 和 If-None-Match
ETag 和 If-None-Match 是 http1.1 新增的字段。与上述的 Last-Modified 和 If-Modified-Since 类似,只是用文件的唯一标识代替了最后修改时间。
浏览器在第一次访问资源时,服务器返回资源的同时,在响应头中添加 ETag 字段,其值是服务器计算该资源的内容得出的 hash。当资源变化时,这个值就会重新生成。
浏览器下一次请求这个资源时,就会携带 If-None-Match 这个字段,其值就是 ETag 中的值。
服务器再次收到这个资源请求,会根据 If-None-Match 中的值与服务器中这个资源的 ETag 对比。如果没有变化,则返回304,浏览器直接从缓存读取资源;如果不一致,说明文件有更新,则返回200和新的资源文件。
ETag 的优先级高于 Last-Modified,且精度高于 Last-Modified。唯一的缺点是计算文件 hash 导致其性能比 Last-Modified 略差。
2. 前端文件Named-Hash
现阶段前端打包时,会在文件名中添加Hash(部分)字段,通过此方式解决浏览器强缓存无法识别文件内容变更场景。
为何不通过给文件设置版本号,在queryString中添加版本号访问指定版本文件原因,可见:https://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/一文。
Named-Hash主要分为以下三种:
hash
webpack中每次构建都会生成一个新的hash,通过这个hash可以更新所有文件。
chunkhash
chunkhash指的是入口文件的hash,每个入口文件都会打包出一个hash值,当入口文件及其依赖更新时,chunkhash就会更新。
contenthash
chunkhash存在这样的问题,当抽离出css文件时,css本身没有变化,但却因为入口文件的改变而使得hash值改变;另一方面,只修改css文件,webpack并不能监听到css文件的变化,从而导致hash值不能更新。contenthash很好地解决了这些问题。contenthash是通过打包出来文件内容计算出来的,只跟文件内容有关。