浅谈浏览器缓存

前言

浏览器的缓存机制是为了节约网络的资源,浏览器在用户磁盘上对最近请求过的文档进行存储,当访问者再次请求这个页面时,浏览器就可以从本地磁盘显示文档,这样就可以加速页面的阅览。其主要的方式是通过HTTP进行一些设置,也有一些非HTTP的方法设置缓存,进行一下记录。

非HTTP方式

在标签中添加上面三行可以设置当前页面不被缓存,但并不是所有浏览器都支持。所以用的多的还是HTTP方式。

HTTP缓存

浏览器缓存的过程是在第一次打开页面时,将页面的css js 图片等内容保存在本地,然后在下一次再请求同样的页面时,直接从本地打开需要的页面。这样是可以极大的提高用户体验,但同时也会有问题,当服务器更新文件之后,如果还显示本地的文件肯定是不对的。这时候就需要使用HTTP中的一些方式来解决了。  主要的方式有三种,一种是服务器告知客户端文件的过期时间,在过期时间前请求该文件都从缓存中读取,而过了这个时间请求就重新去服务器获取;这种方式会有一个问题,就是当过了过期时间后我们应该去请求服务器,但实际上服务器上的文件并没有更新其实还是可以读取缓存的。这个问题可以用第二种方式和第三种方式解决。第二种方式是服务器告知客户端文件的最后更新时间,而浏览器通过每次去对比这个最后更新时间是否变化来判断是否使用本地缓存;第三种方式也是类似的,服务器每次更新文件后改变文件的版本号,然后将当前版本号告知客户端,客户端请求时对比版本号是否一致,一致则读取本地缓存。这三种方式,就是HTTP中Cache-Control/Expires,Last-Modified/If-Modified-Since和ETag/If-None-Match;下面一个个记录。

Cache-Control/Expires

这二者实际上没有什么关联,只是作用类似,都是响应头中的项。二者都是用来约定文件的过期时间。Expires是HTTP1.0的东西,上图可以看到它是直接给定了一个过期时间。而Cache-Control是HTTP1.1里的,它用max-age来规定缓存的秒数(单位是s)。当二者同时存在时,Cache-Control优先。Cache-Control不光可以设置max-age,下图是它可以设置的一些值。

Last-Modified/If-Modified-Since

简单的说,Last-Modified 与If-Modified-Since 都是用于记录页面最后修改时间的 HTTP 头信息,只是 Last-Modified 是由服务器往客户端发送的 HTTP 头,而 If-Modified-Since 则是由客户端往服务器发送的头。浏览器将服务器端发送过来的Last-Modify值和页面内容一起存储至缓存文件中,当再次访问该网页时,把该日期赋给请求报头的If-Modify-Since报头域并传送至服务器,此时服务器判断该日期是否与内容最后修改日期一致,若一致则返回304告诉客户端其本地 cache 的页面是最新的,直接从本地加载页面。若不一致则返回200和新的资源。

ETag/If-None-Match

与Last-Modified/If-Modified-Since类似,只不过Last-Modified和If-Modified-Since只判断资源的最后修改时间,而ETags和If-None-Match可以是资源任何的任何属性,其他工作原理基本相同,也可以理解为上面说的版本号。

不能被缓存的请求

综合上面说的,以下请求不会被缓存

HTTP信息头中包含Cache-Control:no-cache,或Cache-Control:max-age=0等告诉浏览器不用缓存的请求

需要根据Cookie,认证信息等决定输入内容的动态请求是不能被缓存的

POST请求无法被缓存

HTTP响应头中不包含Last-Modified/Etag,也不包含Cache-Control/Expires的请求无法被缓存

浏览器缓存问题补充遇到的问题

当js文件更新后,由于用户浏览器会使用本地缓存的js文件,而不是最新的文件,导致出现报错的问题。

解决办法

强制不缓存

每次更新替换html中引入文件的版本号

使用动态插入脚本的方式进行指定时长缓存

强制不缓存

根据项目情况在tomcat、apache或nginx等web服务器上配置 为 来达到不缓存文件的目的。这种方式直接禁用了所有的浏览器文件缓存,会导致用户每次刷新页面都需要请求后台重新下载文件,从而增长等待时间用户体验很差。

每次更新文件时给文件名添加版本号后缀

这种方式可以解决上一种方式的问题,即不需要每次刷新都重新下载文件,只有更新后才会请求新文件。一般这种解决方案的实现方式是利用webpack等工具给打包后的文件名添加hash或者时间戳作为版本号。该方式适合于html和js、css等静态资源同时上线的项目;如果项目情况是html和其他前端静态资源上线方式不同,例如html需要rd上线而静态资源前端上线,使用该方式则需要大大提高沟通成本,很麻烦。

如果有一种方式能做到每次更新静态资源不需要更新html文件,则可以解决该问题。下面要说的方式能基本解决。

使用脚本动态插入script、link标签

使用脚本插入的方式可以做到向script和link标签的路径中插入指定的版本号,如时间戳等。

实现方式一代码如下:

如上代码使用 方法向文档中插入了一个 标签,并且 属性后面拼接了当前时间做为版本号。该方法存在一些问题,例如当页面dom结构很复杂时, 方法的参数很长很混乱不易维护。还有 方法本身就存在一个问题,即当页面加载完成后执行 会覆盖原有的文档,重写所有内容,可能导致head里的内容丢失。所以一般使用 的方式来做。

实现方式二代码如下:

该方式在解决上述问题的同时,也带来的另一个问题:该函数插入js时的加载顺序无法保证,所以需要将 属性设置为 令其同步加载,然后按依赖顺序插入。

最后,上面的代码中是使用 来做为版本号的,也就是说每次刷新页面该时间戳都会改变,所以实际上是和强制不缓存效果一样。但是如果我们使用指定的时长做缓存,比如把 改成:

使用 也就是今天的时间,那么该文件将缓存一天。第二天首次加载则重新下载最新的文件,你也可以根据自己的需求修改这个时长。

最后结论

该方式可以在保证良好用户体验的同时,解决由于浏览器缓存导致更新js等静态资源不生效的问题。

  • 发表于:
  • 原文链接:http://kuaibao.qq.com/s/20171213G0YVVA00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券