前端性能优化中一个很重要的方面就是充分利用缓存,比如通过cache-control, expires, eTag等来控制静态资源失效时间,很多大型网站的静态资源实现实现甚至到了几年。这确实提高了页面加载速度,但同时引来另一个问题,我更改了静态资源,如何让客户端更新缓存。
思路
最有效的解决方案是修改其所有链接,这样,全新的请求将从原始服务器下载最新的内容。
没错,但是怎样更新链接?
来看看一般前端团队的做法
index.html页面里:
<script charset="utf-8" type="text/javascript" src="a.js?t=20131111"></script>
大家会采用添加query的形式修改链接。这样做是比较直观的解决方案,但在访问量较大的网站,这么做可能将面临一些新的问题,因为在发布的过程中,index.html和a.js总有一个先后的顺序,从而中间出现一段或大或小的时间间隔。对于一个大型互联网应用来说即使在一个很小的时间间隔内,都有可能出现新用户访问。这些用户访问到的页面和js是不匹配的,会导致页面出错。
这就是为什么大型web应用在版本上线的过程中经常会较集中的出现前端报错日志的原因,也是一些互联网公司选择加班到半夜等待访问低峰期再上线的原因之一。此外,由于静态资源文件版本更新是“覆盖式”的,而页面需要通过修改query来更新,对于使用CDN缓存的web产品来说,还可能面临CDN缓存攻击的问题。
当版本有更新时,修改所有引用链接也是一件与工程管理相悖的事,至少我们需要一个可以“查找-替换”的工具来自动化的解决版本号修改的问题,要知道手动去修改ts是多少让人崩溃的事情,特别是对于频繁发布的情况。
针对这个问题,目前最优的解决方案就是基于文件内容的hash版本号,也就是说,将文件内容进行hash,用hash值来代替时间戳。这么做的好处,文件不变的时候,版本号不变,文件变了,版本号自动更新。
也就是,发布的时候
<script charset="utf-8" type="text/javascript" src="a.js"></script>
<script charset="utf-8" type="text/javascript" src="a.js?t=ba9c11c7"></script>
已发布到npm,npm install asset-cache-control下载
问题
上面的做法,其实还是改静态资源query值,并没有解决发布过程中的资源覆盖先后问题,对于大型网站,做下面的修改更合适。
<script charset="utf-8" type="text/javascript" src="a_ba9c11c7.js"></script>