1.浏览器缓存的基本认识分为强缓存和协商缓存。**,浏览器直接从自己的缓存中读取资源,不向服务器发送请求。比如某个css文件,如果浏览器加载它所在的网页,css文件的缓存配置开启了强缓存,浏览器直接从缓存中加载css,连请求都不会发送到网页。当服务器没有强缓存时,浏览器肯定会向服务器发送请求,通过服务器根据资源的其他http头来验证资源是否协商缓存。如果协商的缓存有效,服务器会返回这个请求,但不会返回这个资源的数据,而是告诉客户端可以直接从缓存中加载这个资源,所以浏览器会从自己的缓存中加载这个资源.重点是:如果***,资源是从客户端缓存加载的,而不是从服务端加载资源数据;不同的是:强缓存不向服务器发送请求,协商缓存在没有协商缓存时会向服务器发送请求*激活时,浏览器直接从服务器加载资源数据。2.强缓存原理2.1简介当浏览器请求某个资源开启强缓存时,返回的http状态为200,chrome开发者工具的网络中大小会显示为fromcache。比如京东首页有很多配置了强缓存的静态资源。用chrome打开几次,然后f12检查网络。可以看到很多请求都是从缓存中加载的。强缓存是使用两个http响应头Expires或Cache-Control来实现的,这两个头都用于指示客户端缓存中资源的有效期。expires是http1.0提出的一个header,用来表示资源过期时间。描述了一个绝对时间,由服务器返回,用GMT格式的字符串表示,如:Expires:Thu,31Dec203723:55:55GMT2.2Expires缓存原理浏览器第一次请求资源从服务端,服务端在返回资源的同时会在响应头中加上Expires。如果浏览器收到资源,会把资源连同所有响应的headers一起缓存起来(所以缓存***请求返回的header不是来自服务器,而是来自之前缓存的header)当浏览器请求时再次获取这个资源,它首先从缓存中查找,找到资源后,取出Expires与当前请求时间进行比较。如果请求时间早于Expires指定的时间,则可以激活缓存,否则不起作用。如果没有激活缓存,当浏览器直接从服务器加载资源时,会重新加载ExpiresHeaderExpires是一个比较旧的强缓存管理头。由于是服务端返回的绝对时间,当服务端时间与客户端时间相差较大时,缓存管理容易出现问题,如随意修改客户端时间,会影响缓存攻击的结果。所以在http1.1中,提出了一个新的header,就是Cache-Control。这是一个相对时间。配置缓存时,以秒为单位表示数值,如:Cache-Control:max-age=3153600002.3Cache-Control缓存原理浏览器第一次向服务器请求资源时,服务器返回资源,并在响应头中加入Cache-Control,如:浏览器收到这个资源,最后会将这个资源和所有响应头一起缓存起来。当浏览器再次请求该资源时,会先从缓存中查找。找到这个资源后,根据它的第一次请求时间和Cache-Control设置的有效期,计算一个资源过期时间,然后把这个过期时间和当前请求时间进行比较。如果请求时间在过期时间之前,可以激活缓存,否则不起作用。如果缓存没有被激活,浏览器直接从服务器加载资源,当重新加载资源时,Cache-ControlHeader会在重新加载时更新。Cache-Control描述了一个相对时间。在执行缓存激活时,是通过客户端时间来判断的,所以相对于Expires,Cache-Control的缓存管理更加高效和安全。只能启用这两个标头中的一个,也可以同时启用它们。当响应头中Expires和Cache-Control同时存在时,Cache-Control的优先级高于Expires:3.强缓存管理前面介绍的是强缓存原理,在实际应用中,我们会遇到需要强缓存的场景缓存和不需要强缓存的场景。通常,有两种方式可以设置是否启用强缓存,即在Web服务器返回的响应中添加Expires和Cache。-ControlHeader通过配置web服务器,让web服务器在响应资源时统一加上Expires和Cache-ControlHeader。比如在javaweb中,我们可以使用类似下面的代码来设置强缓存java.util.Datedate=newjava.util.Date();response.setDateHeader("Expires",date.getTime()+20000);//Expires:过期时间限制值response.setHeader("Cache-Control","public");//Cache-Control控制页面是否缓存,public:浏览器和缓存服务器可以缓存页面信息;response.setHeader("Pragma","Pragma");//Pragma:设置页面是否缓存,如果是Pragma则缓存,no-cache如果不缓存,也可以通过设置强缓存Java代码如下response.setHeader("Pragma","no-cache");response.setDateHeader("Expires",0);response.addHeader("Cache-Control","no-cache");//浏览器和缓存服务器不应该缓存页面信息。作为专业的web服务器,nginx和apache都有专门的配置文件,可以配置expires和cache-control。这方面的知识,有兴趣运维的可以百度搜索nginxsettingexpirescache-control或者apachesettingexpirescache-control可以找到很多相关的文章。因为开发时不会特别配置强缓存,浏览器默认会缓存图片、css、js等静态资源。所以在开发环境中,经常会因为强缓存导致资源更新不及时,看不到最新的效果。有很多方法可以解决这个问题。以下方法通常用于处理缓存引起的问题。该方法可以解决页面直接引用资源更新的问题。使用浏览器的隐私模式进行开发。如果你使用chrome,你可以用f12禁用网络中的缓存(这是一个非常有效的方法)。开发阶段,在A动态参数中添加资源,如css/index.css?v=0.0001,由于每次修改资源都需要更新引用位置和修改参数的值,操作不是很方便,除非你是在jsp之类的动态页面上,你可以使用服务器变量来解决(v=${sysRnd}),或者你可以使用一些前端构建工具来处理参数修改的问题。如果资源引用的页面嵌入在iframe中,你可以在iframe中使用鼠标右键点击区域重新加载页面。以chrome为例,如果ajax请求出现缓存问题,最有效的解决方法是在ajax请求地址中添加一个随机数。另一种情况是在动态设置iframe的src时,也有可能因为缓存的问题,看不到***效果。这时候在要设置的src后面加上随机数也可以解决问题。如果使用grunt、gulp、webpack等前端工具进行开发,通过它们的grunt-contrib-connect等插件启动静态服务器,开发阶段无需担心资源更新,因为在这个静态服务器下的所有资源返回的responseheader中,cache-control总是设置为不缓存4.强缓存的应用强缓存是前端性能优化最有力的利器。对于静态资源较多的网页,必须使用强缓存来提高响应速度。通常的做法是为所有这些静态资源配置一个超时时间较长的Expires或Cache-Control,这样当用户访问一个网页时,只会在第一次加载时向服务器请求静态资源。无效时会从自己的缓存中加载,用户不强制刷新。问题是发布时的资源更新问题。例如,某张图片在用户访问第一个版本时已经缓存在用户的电脑上。当网站发布新版本并更换图片??时,它已经被访问过。由于缓存的设置,第一个版本的用户默认不会向服务器请求***图片资源。除非他清除或禁用缓存或强制刷新,否则他将看不到***图像。因此,这个问题已经有成熟的解决方案。具体可以看知乎上的这篇文章:http://www.zhihu.com/question/20790576文中提到的东西都是理论上的解决方案,但是现在已经有很多前端工具可以实际解决这个问题。由于每个工具都涉及很多细节,本文无法深入介绍。有兴趣的可以了解gruntgulpwebpackfis和edp工具。这些工具可以解决这个问题,尤其是fis和edp是百度推出的前端开发平台。有现成的文档可以参考:http://fis.baidu.com/fis3/api/index.htmlhttp://ecomfe.github.io/edp/doc/initialization/install/关于strong还有一个需要注意的地方缓存就是通常是给静态资源使用,动态资源需要慎用,除了服务端页面可以看成是动态资源,那些引用静态资源的html也可以看成是动态资源,如果这样的话html也会被缓存,当这些html更新时,可能没有机制通知浏览器这些html已经更新了,尤其是在前后端分离的应用中,页面都是纯html页面,并且每个访问地址都可以直接访问html页面。这些页面通常不会加强缓存,以保证浏览器在访问这些页面时,总是向服务器请求最好的资源。5.协商缓存原理5.1简介当浏览器对资源请求没有强缓存时,会向服务器发送请求,验证协商缓存是否有效*,如果协商缓存***,则请求响应返回的http状态为304,会显示一串NotModified。比如打开京东首页,按F12打开开发者工具,然后按F5刷新页面查看网络。可以看到有很多请求会检查单个请求的ResponseHeader进行协商和缓存。还可以看到304的状态码和NotModified的字符串。只要看到这个,就说明这个资源是***5.2Last-Modified,If-Modified-SinceControlNegotiationCache浏览器第一次向服务器请求资源,服务器返回这个同时作为资源,将Last-Modified标头添加到响应标头。此标头表示服务器上资源的最新修改时间。当浏览器再次向服务器请求该资源时,在请求头中添加If-Modified。-Sinceheader,这个header的值为上次请求返回的Last-Modified值。服务器再次收到资源请求时,会根据浏览器发送If-Modified-Sinc。e和资源在服务器上的最新修改时间来判断资源是否发生变化。如果没有变化,则返回304NotModified,但不返回资源内容;如果有变化,当服务器返回304NotModified时资源内容会正常返回。响应时,不会在响应头中加入Last-Modified头,因为既然资源没有变化,那么Last-Modified也不会改变。这是服务器返回304时的响应头,浏览器收到304响应后,会从缓存中加载资源。如果协商缓存无效,当浏览器直接从服务器加载资源时,重新加载时会更新Last-ModifiedHeader,并在下一次请求时启用If-Modified-Since。上次返回的Last-Modified值[Last-Modified,If-Modified-Since]是根据服务器时间返回的header。一般来说,在不调整服务器时间和不篡改客户端缓存的情况下,两个header配合管理协商缓存似乎很靠谱,但有时服务器上的资源其实是变化的,但是***修改时间不会改变,这种问题不容易定位,当这种情况出现时,会影响协商缓存的可靠性。因此,还有另一对标头来管理协商缓存。这对标头是[ETag,If-None-Match]。他们的缓存管理方式是5.3ETag,If-None-Match控制协商缓存浏览器第一次向服务器请求一个资源,服务器在返回资源的同时在响应头中加上ETag头。它是服务器根据当前请求的资源生成的唯一标识符。这个唯一标识符是一个字符串。只要资源发生变化,字符串就会不同。与***修改时间无关,可以很好的补充Last-Modified问题。当浏览器再次向服务器请求此资源时,它会在请求的标头中添加一个If-None-Match标头。这个header的值就是之前请求返回的ETag的值。通过If-None-Match,然后根据资源生成新的ETag。如果两个值相同,说明资源没有变化,否则有变化;如果没有变化,则返回304NotModified,但不返回资源内容;如果有变化,则正常返回资源内容。与Last-Modified不同的是,当服务器返回304NotModified响应时,由于已经重新生成了ETag,因此即使ETag与之前的没有变化,也会在响应头中返回ETag。浏览器收到304响应后,会从缓存中加载资源。六、协商缓存的管理协商缓存不同于强缓存。强缓存不向服务器发送请求,所以有时候浏览器并不知道资源什么时候更新,但是协商缓存会向服务器发送请求,所以资源有没有更新,服务器肯定知道.大多数web服务器默认开启协商缓存,同时开启[Last-Modified,If-Modified-Since]和[ETag,If-None-Match],比如apache:如果没有协商缓存,则每次向服务器请求,必须返回资源内容,这样服务器的性能会极差。[Last-Modified,If-Modified-Since]和[ETag,If-None-Match]一般是同时开启的,这是为了应对不可靠的Last-Modified。有一个场景需要注意,分布式系统中多台机器之间文件的Last-Modified必须一致,以免负载均衡到不同机器造成比较失败;在分布式系统中尽量关闭ETag(每台机器生成的ETag会不同);对于京东页面的资源请求,返回的repsones头只有Last-Modified,没有ETag:协商缓存需要和强缓存配合使用。在前面的截图中可以看到,除了Last-Modifiedheader之外,还有强缓存相关的headers,因为如果不开启强缓存,协商缓存根本没有意义七、相关浏览器行为对缓存的影响如果资源已经被浏览器缓存,在缓存失效之前,再次请求时,默认会检查是否是***强缓存,如果是强缓存***,直接读取缓存,如果是强缓存没有***,向服务器发送请求,检查是否安装了协商缓存,如果协商缓存***,则告诉浏览器它仍然可以从缓存中读取,否则***资源从服务器返回。这是默认的处理方式,这种方式可能会被浏览器的行为改变:当ctrl+f5强制刷新网页时,直接从服务器加载,跳过强缓存和协商缓存;f5刷新网页时,跳过强缓存,但会检查协商缓存
