Caches作用
Web缓存可以保存常见文档副本,当发起Web请求时,如果本地有 “已缓存的" 的副本,就可以直接从本地存储中提取这个文档,而不是去从原始服务器中拿。
减少冗余数据传输
当很多客户端访问原始服务器页面时,服务器会多次传输同一份文档,相同的数据在网络中一遍遍的传输。这些冗余数据会耗尽昂贵的网络带宽,降低传输速度。
使用缓存之后,浏览器只要保留第一条服务器响应的副本,后续对这个资源的访问都可以从缓存副本中得到了,减少了重复流量。
缓解带宽瓶颈
网络为本地网络客户端提供的带宽比服务器提供的带宽大很多。客户端会以路径上最慢的速度访问服务器。如果客户端从本地或者局域网的缓存中得到了一份副本,那么缓存就可以提高性能,尤其在传输大文件时。
降低原始服务器负载
当很多人去访问原始服务器时,会产生巨大流量,从而可能导致服务器崩溃
降低距离时延
将缓存放在本地或者局域网内可以大幅度降低传输距离,加快传输速度。
浏览器缓存过程
Web缓存服务器的基本工作原理很简单,对一条HTTP GET请求的缓存过程如下:
· 接收:缓存从网络中读取抵达的请求报文
· 解析:解析报文,提取首部
· 查询:查询本地是否有副本,如果没有,就发起请求获取副本
· 新鲜度检测:查看本地缓存是否足够新鲜,如果不是,则查询服务器是否有更新
· 创建响应:返回缓存副本
· 发送:将响应发回给客户端
对于浏览器而言,整个过程又可以简化:
· 查询本地副本,如果有且足够新鲜,则直接使用本地副本
· 否则发起请求,验证本地副本是否足够新鲜
以上两个过程分别称为强缓存和协商缓存。
强缓存
强缓存即直接从本地读取缓存副本,不需要发起请求。这种方式是最快的。
强缓存通过HTTP 首部:cache-control管理
cache-control首部
cache-control首部用来指定缓存策略,例如是否能缓存,谁可以缓存,缓存多久
no-cache与no-store:
no-cache:表示必须先与服务器确认返回的响应是否被更改,然后才能使用该响应来满足后续对同一个网址的请求。如果资源未被更改,可以避免下载。
no-store:禁止浏览器 (和所有中继缓存)存储返回的 任何版本的响应,每次用户请求该资源时,都会向服务器发送一个请求,每次都会下载完整的响应
public 与 private:
public:即使有关联的 HTTP 认证,甚至响应状态码无法正常缓存,响应也可以被缓存
private:不允许任何中继缓存对其进行缓存
max-age指令
指定从当前请求开始,允许获取的响应被重用的最长时间(单位为秒)。
通过cache-control的max-age,设置好缓存时间,单位是秒,例如缓存一年:
- cache-control: public, max-age=31536000
当使用本地强缓存时,浏览器发回的响应信息是 200 from cache 或者 200 from memory cache,所耗费的时间在几毫秒之间,可以忽略不计。
强缓存的更新问题:如果资源有更新,则希望浏览器使用新的资源。但是在现如今情况下,除了修改资源名称,没有别的办法更新资源。所以现在通常使用的办法是更新资源名称,例如资源名称一般是:
- [name]-[hash].ext
在原始文件名之后加入文件哈希值,这样当文件内容修改之后,哈希值就会改变。再修改html文件的引用地址,这样就可以做到资源更新时,自动下载新资源;否则会一直使用本地缓存。
expires首部
expires是HTTP/1.0时期用于强缓存的首部字段。它会指定一个绝对的过期日期,如果日期已经过了,说明本地副本不再新鲜了。例如:
- Expires: Fri, 09 Dec 2016, 05:00:00 GMT
Expires也是精确到秒的。
由于expires使用的是绝对日期,如果服务器与客户端时间不一致,则会出现缓存不成功或者缓存不更新的问题。所以现在都建议cache-control做强缓存。
协商缓存
当本地缓存过期的时候,浏览器就会尝试协商缓存。协商缓存会发起请求,以验证本地副本是否可以使用。也称为再验证的过程。
· 如果验证后显示文档内容变化,浏览器会获取一份新的副本,替代旧副本,缓存在本地
· 如果验证后显示文档内容无变化,浏览器只用获取新的首部和新的过期日期
- 根据响应中的状态码来指明文档没有变化。304 Not Modified
验证文档内容是否变化有两种方式,统称为条件GET方法
Last-Modified 与 If-Modified-Since首部
这一对首部通过资源修改日期判断是否有变化,主要过程为:
· 首次获取资源:服务器响应中添加首部Last-Modified,说明资源最近一次修改时间。浏览器保存下来这个时间
· 验证资源:浏览器带上If-Modified-Since首部,值就是Last-Modified返回的时间;服务器比较If-Modified-Since的时间与资源最近一次修改时间:
o 如果最近一次修改时间小于If-Modified-Since的时间,则返回304响应,响应中也会有cache-control、expires、Date首部;
o 如果最近一次修改时间大于If-Modified-Since的时间,则返回新资源,并且更新所对应的全部首部,包括cache-control、expires、Date
ETag与If-None-Match首部
ETag是服务器为资源生成的唯一标识字符串。只有当文件内容变化时,ETag才会变化。使用过程与Last-Modified类似。
· 首次获取资源:浏览器得到响应首部ETag对应的值
· 验证资源:带上If-None-Match首部,值就是浏览器从服务器获得的ETag值,这样服务器可以比较ETag值来确定客户端缓存是否是最新,如果不是最新,则返回新文档;如果是,直接返回304Not Modified,包括cache-control、expires、Date首部
ETag对比Last-Modified优势
在有些情况下,Last-Modified会出现难以解决的问题:
· 有些文档可能会被周期性的重写,但实际内容并没有变化。而此时Last-Modified会变化,ETag则不会
· 有些文档被修改了,但是修改的内容并不重要,比如注释,不需要更新缓存
· 有些服务器无法准确判断页面最后修改日期
· 有些服务器的文档可能在1秒钟之内就产生变化了,而Last-Modified只能精确到秒,在某些场合下不够精准
如果同时使用ETag和Last-Modified两种验证方式,服务器会优先验证ETag对应的If-None-Match请求首部,如果ETag相同,才会去验证Last-Modified
项目中遇到问题的解释:
如果设置的是no-store,浏览器每次都会请求新的;如果是no-cache,浏览器会优先走304协商缓存;如果这两个都没设置,而是设置了max-age,那在过期时间之内,浏览器不会发送请求到服务器,直接使用本地的。当然如果是cmd+r刷新,是会请求新资源的。
如果不向服务器请求,浏览器就拿不到新资源的。form请求能拿到返回code,但是返回值我们是拿不到的;ajax可以拿到。(所以那个上传文件的组件,form请求失败,需要前端重定向)所以form没有跨域问题,而ajax有跨域问题。而跨域是说请求发送到了服务器,而我们拿不到返回值而已。浏览器觉得跨域拿返回值是危险的。window.open和输入url是一样的。
如果后端正确设置了index.html的cache-control规则,是不需要强刷的。后端对index.html设置cache-control: no-cache,no-store,浏览器每次都请求新的html资源,这样用户就会请求新的js和css了(上次是这个规则没设置,所以要强刷)。如果强缓存未过期,在最近一段时间内加载过这个文件,就会from memory,否则就是from disk;from cache也是从缓存里读取。最终都是强缓存,没有请求服务器。304是向服务器做了协商缓存验证,如果没更新才会使用本地资源。
(上次那个没权限用户总是走缓存的问题,meta头,后端没设置。所以后面自己设置了,并且在登录回调那里把index.html加上时间戳。)