前言相对于之前的html开发阶段,现在的前端项目一般都是打包,然后部署到服务器,最后供用户访问。打包部署过程的可玩性大大提高,引入了前端工程管理的很多内容,比如多环境支持、动态配置、打包速度、访问加速等等。今天只讨论资源文件的访问加速。C端大概率需要使用CDN加速,B端一般把资源放在服务器上。这两者都将涉及浏览器缓存。那么如何利用浏览器的缓存机制来实现我们的加速呢?你需要对浏览器缓存有一定的了解。浏览器缓存浏览器缓存(BrowerCaching)是浏览器将用户最近请求的文档存储在本地磁盘上。当访问者再次访问同一页面时,浏览器可以直接从本地磁盘加载文档。一是减轻服务器压力和数据传输,节省网站压力和带宽。二是加快客户端使用速度,提升系统体验。浏览器缓存主要有两种:缓存协商和彻底缓存,也称为“协商缓存”和“强缓存”。浏览器第一次请求后,再次请求时:浏览器会先获取资源缓存的头部信息,根据其中的Expires和Cache-control判断是否命中强缓存。如果命中,则直接从缓存中获取资源。包括缓存的头部信息,本次请求不会与服务器通信;如果没有命中强缓存,浏览器会向服务器发送请求,请求中会携带第一次请求返回的缓存头域信息(Last-Modified/IF-Modified-Since,Etag/IF-None-Match),服务器会根据请求中的相关头信息进行比较,看是否命中协商缓存,命中则返回新的响应头信息更新缓存中对应的头信息,但不返回资源内容,它会告诉浏览器可以直接从缓存中获取;否则,返回最新的资源内容。你能理解强缓存就是在本地获取缓存,失败了再协商缓存吗?强缓存和强缓存相关的头字段是Cache-Control和Expires。这里我们重点关注Cache-Control,也就是http1.1中出现的header信息。它的值如下:max-age:重要!是一个相对时间,比如Cache-Control:max-age=3600,表示该资源有效期为3600秒。no-cache:不使用本地缓存。需要使用缓存协商与服务器确认返回的响应是否发生了变化。如果前面的响应中有ETag,则请求将与服务器进行验证。如果资源没有改变,可以避免重新下载。no-store:直接禁止浏览器缓存数据。用户每次请求资源时,都会向服务器发送一个请求,每次都会下载完整的资源。public:可以被所有用户缓存,包括终端用户和CDN等中间代理服务器。private:只能被终端用户的浏览器缓存,不允许CDN等中继缓存服务器缓存。现在知道no-cache和no-store的区别了,面试就别再犯错误了。可以在服务器配置中同时启用Cache-Control和Expires。同时启用时,Cache-Control的优先级更高。推荐使用Cache-Control。对于这两个header,可以这样理解:Expires是http1.0规范的内容,Cache-Control是http1.1规范的内容。Expires的过期时间是本地时间,在多个时区不可靠,改成时间长度。前后端类似倒计时的设计,是unix时间戳+相对时间长度协商缓存协商缓存可以理解为通过协商机制实现缓存同步。该机制的核心是配对的响应头和请求头。协商步骤为:第一次请求资源时,浏览器返回响应头及其值;当再次请求该资源时,浏览器会添加相应的请求头和之前响应头中的值,服务会判断是否命中缓存以及后续不同的操作,浏览器缓存的文件会有不同的缓存存储.所以使用不同的字段来协商,它们的关系如下:响应头(Last-Modify)/请求头(If-Modify-Since):缓存在内存中(frommemorycache),它的值是这样的一个时间如格林威治标准时间2037年12月31日星期四23:59:59。响应头(ETag)/请求头(If-None-Match):缓存到硬盘(fromdiskcache),其值为校验码。下面说说两者的具体细节和区别。1.If-Modified-Since只能用在GET或HEAD请求中。命中缓存返回304,不返回资源内容,不返回Last-Modify。2、ETag响应头是资源特定版本的标识,可以保证每一个资源都是唯一的,资源的变化会引起ETag的变化。If-None-Match是一个条件请求头。对于GET和HEAD请求方法,当且仅当服务器上没有任何资源的ETag属性值与此标头中列出的匹配时,服务器将返回请求的资源,响应代码为200。对于其他方法,当且仅当最终确认不存在具有与此标头中列出的ETag属性值匹配的现有资源时,才会相应地处理请求。3、If-None-Match和If-Modified-Since一起使用时,前者优先级高。If-None-Match有以下优点:因为如果内容没有改变,Web服务器不需要发送完整的响应,并且如果内容已经改变,使用ETag有助于防止资源的同时更新相互覆盖变了。与Last-Modified不同的是,当服务器返回304NotModified响应时,由于ETag已经重新生成,响应头将返回这个ETag,即使ETag与之前的没有变化。Last-Modified标记的最后一次修改只能精确到秒级,当文件在1秒内被多次修改时就变得不可靠了。但是,ETags可以通过多种方式生成,例如资源内容的防碰撞哈希函数生成的哈希值,最后修改时间戳的哈希,以及资源的版本号。至此,协商缓存的机制和细节大家就明白了吧?综上,一个新的问题出现了:当响应头同时有Last-Modified和Etag时,Etag是不是多余的?一些文件可能会周期性的改变,但是它们的内容是不会改变的(只有修改的修改时间)。这个时候,我们不希望客户端认为文件被修改了,再次请求;一些文件修改非常频繁。比如秒级内修改(比如1s内N次),If-Modified-Since可以检查的粒度是秒级,无法判断这种修改(或者UNIX记录MTIME只能精确到秒);部分服务器无法准确获取文件的最后修改时间。Last-Modified和ETag可以一起使用。服务端会先校验ETag,如果一致则继续比较Last-Modified,最后决定是否返回304。《强缓存和协商缓存的区别》缓存类型获取资源表单状态码向服务器发送请求告知缓存是否可用《用户行为对缓存的影响》用户操作Expires/Cache-ControlLast-Modied/Etag地址栏输入有效有效页面链接跳转有效有效新窗口有效有效前进后退有效F5刷新有效有效Ctrl+F5有四个地方强制刷新无效无效浏览器缓存。当依次查找缓存都没有命中时,就会请求网络。更多介绍请看深入理解浏览器的缓存机制ServiceWorker:由开发者控制的缓存位置,缓存哪些文件,如何匹配缓存,如何读取缓存,缓存是持久化的。MemoryCache:一旦我们关闭了Tab页,内存中的缓存就被释放了。DiskCache:是MemoryCache的补充,不同的浏览器有不同的策略。PushCache:是HTTP2中的内容,只存在于会话(Session)中,一旦会话结束就会释放,而且缓存时间也很短,nocacheno-store上面说的强制缓存Cache-control指令no-store,作用是不使用任何缓存,配置为:Cache-Control:no-store。需要注意的是,Cache-Control是一个通用的消息头字段,请求头和响应头都可以使用。以下是来自MDN的一些额外示例,以说明如何配置其他场景。1、缓存静态资源:Cache-Control:public,max-age=31536000,对于在应用中不会改变的文件,通常可以在发送响应头之前加上激进的缓存。例如,这包括应用程序提供的静态文件,例如图像、CSS文件和JavaScript文件。2.Revalidationisrequired:Cache-Control:no-cacheorCache-Control:max-age=0,must-revalidate表示客户端可以缓存资源,每次使用前必须重新验证缓存资源的有效性.这意味着每次都会发出HTTP请求,但是当缓存的内容仍然有效时,可以跳过HTTP响应主体的下载。————可以理解,改变默认的缓存机制,将“强缓存→协商缓存”改为“协商缓存→强缓存”。3、注意当服务器关闭或连接丢失时,Cache-Control:max-age=0可能会使用本地缓存。增加版本号的方法不需要依赖服务端,用纯前端就可以实现。这种方法在前端工程诞生之前就很流行。缺点是需要手动增加版本号,人为干预较多。使用随机数,因为在文件后添加指纹允许浏览器重新获取resources,那么我们可以在后面拼接随机数或者时间戳,也可以达到同样的目的,同时也省去了手动修改版本号的步骤。具体可以在index.html中添加脚本,动态生成脚本标签,导入静态资源,拼接时间戳。这样浏览器每次刷新都会动态生成一个包含时间戳的静态资源。当浏览器发现文件名发生变化时,会重新获取静态资源,达到不缓存文件的目的。varscript=document.createElement("script");script.src="/resource/options/myjs.js?randomId="+newDate().getTime();document.body.appendChild(script);其实现在打包工具增加哈希值就是利用了这个思路。使用HTML禁用缓存HTML也可以通过向页面的head标记添加元标记来禁用缓存。示例:
