先来个最原始的页面,比如下面的index.html中,引入了css和js资源
<link rel="stylesheet" href="a.css"></link>
<script src="a.js"></script>
写完后,部署上线。然而我们要考虑充分利用浏览器缓存,我们的目标是资源文件直接读取浏览器缓存,于是给它设置 Cache-Control/Expires 和 Last-Modified/ETag。Cache-Control/Expires的作用是在缓存时间内,正常进入页面,浏览器不会发出请求,直接读取浏览器缓存资源。如果用户点击“刷新”按钮或缓存时间消失,浏览器会发送请求,并根据Last-Modified/ETag判断内容是否有更新,如果内容没更新,服务器返回304。
上线后,来了一大波需求。于是改完再发一次。依然是直接将html和资源文件丢到线上,直接访问页面地址,咦?怎么没生效?哦,原来是缓存作祟。html更新了,但是读取的a.css和a.js还是浏览器缓存里面的内容,没有更新到最新。怎样让浏览器缓存失效呢?于是我们想到了版本控制,加个版本号不就解决问题了嘛,so easy!于是代码变成了这样
<link rel="stylesheet" href="a.css?v=0.01"></link>
<script src="a.js?v=0.01"></script>
下次更新的时候,升级版本就行了。恩,浏览器缓存导致更新不生效的问题终于解决了。但是新的问题又来了:
- 管理成本增加,每次发布要记得去升级版本号
- 缓存丢失,只是修改了a.css,结果a.js的版本也要升级,a.js的缓存也跟着丢失
不能忍,继续寻找新的解决方案。最优的方案是根据文件内容,生成一串hash值。通过hash值来判断文件内容是否有产生变化。于是,代码成了这样:
<link rel="stylesheet" href="a.css?v=e0279"></link>
<script src="a.js?v=abb35"></script>
问题到这似乎解决了,终于可以歇口气。图样图森破,这里有个新的问题。
我们发布项目的时候,资源文件和主文件是分开发的,资源文件部署在CDN中,主文件部署在服务器,这时是先发CDN还是先发主文件呢?
- 先发资源文件,之前的资源文件被覆盖,在主文件发布成功之前,没有缓存或强制刷新的用户,会导致页面错乱
- 先发主文件,在资源文件发布成功之前,用户访问到得资源文件都是旧的
两种方式都有问题,继续调整。上面问题的原因是由于资源是覆盖式发布,如果将资源文件变成非覆盖式发布,新版和旧版资源文件共存,就可以有效解决了。代码再一次华丽转变:
<link rel="stylesheet" href="a.e0279.css"></link>
<script src="a.e0279.js"></script>
发布时先发资源文件,资源文件部署成功后,在发主文件,就可以有效避免上面问题了。
至此问题已经得到了一个比较好的解决方案。至于怎么去给资源名加上hash值,这个就是构建的事了。构建的时候需要注意任务顺序,通常需要加hash值的资源文件有js、css和img,而css和js是可以引入img的,所以需要先对img进行hash值处理,然后整站替换新的img路径。再对css和js hash值处理,处理完后,在html文件中替换新的资源路径。