以MD5值命名静态资源的必要性

勿忘初心2018-09-04 10:57

作者:梁枫


网站性能优化中比较重要的一点是高效利用浏览器缓存,浏览器缓存可以减少网络请求数和流量,用以提高页面加载速度。

高效利用浏览器缓存的原则是:只有当文件内容变化时,缓存才失效

现状

但是大多数网站都没有做到这点,因为他们的方式是给静态资源添加版本号,然后开始永久缓存

这个版本号有些是开发者手工维护,有些是后端动态提供。版本号多数是时间戳或是序号的形式。

.bg{
    background: url(../img/bg.png?20150124) no-repeat;
}

<script src="<%= cdnURL%>/js/index.js?<%= cdnVersion%>"></script>

手工维护虽然可以实现以上原则,但是手工维护的成本高,而且有被遗漏的风险。

后端多数会提供一个统一的版本号,所有静态文件共用这一个版本号。所以一旦发版,静态文件都会更换版本号,导致缓存全部失效。而多数公司一周至少发一次版,静态文件实际上只能缓存几天。

尝试优化

面对上述状况,我们尝试去优化,版本号就是优化的切入点,我们需要解决的问题如下:

  1. 自动添加版本号
  2. 每个静态文件对应各自的版本号
  3. 只有当文件内容变化时版本号才能改变

这三个问题都很好解决,解决方案如下:

  1. 引入前端构建工具
  2. 用静态文件的MD5值做版本号

使用MD5作为版本号,可以很好的解决问题2和3,MD5作为信息摘要可以很好的反映文件内容是否改变。而引入前端构建工具,在发布时,执行构建分析每个引用的静态文件的内容,生成MD5值,然后追加到引用地址后面。

还不够好

上面的方案看起来很美,但是问题也是有的,只不过很隐蔽。通过上述方法,我们可以得到发布后的静态文件引用结果:

.bg{
    background: url(../img/bg.png?d41d8cd98f00b204e9800998ecf8427e) no-repeat;
}

<script src="<%= cdnURL%>/js/index.js?1055d3e698d289f2af8663725127bd4b"></script>

不完美的地方是MD5值以链接参数的形式出现,这样做的隐患有以下两个

首先静态文件必须先于模板文件发布。因为如果模板文件先发布,那模板文件中的静态文件链接如果被用户访问了,得到的内容还是旧的,浏览器缓存就把旧的内容缓存起来,导致了静态文件就算发布了,也不会生效。而且如果CDN服务器也开启了服务器缓存,那无论什么时候访问,内容都是旧的。

其次测试时,如果切换错host到线上CDN服务器的host,就会导致包含新MD5值的链接提前被线上CDN服务器缓存起来(如果CDN服务器开启了服务器缓存),而线上CDN服务器上的静态文件是旧的,所以就导致发布静态文件不会生效,而且这个问题也不好排查。

终极解决方案

总结上述两个隐患,其原因就在于:MD5值作为链接参数。解决方案很简单,就是以MD5值作为文件名的一部分,发布后的静态文件引用如下:

.bg{
    background: url(../img/bg_d41d8cd98f00b204e9800998ecf8427e.png) no-repeat;
}

<script src="<%= cdnURL%>/js/index_1055d3e698d289f2af8663725127bd4b.js"></script>

这样做就可以解决上述两个问题,好处是显而易见的,如果修改后的文件没有发布到CDN服务器,那访问对应的链接会报404错误,结果不会被服务器和浏览器缓存。当然如果发布的时候,先发模板文件,那在静态文件发布前,用户访问页面时会出现静态文件访问不到的情况。考虑到这个时间间隔不会太大,并且多数发布都是在用户访问不频繁的时间,所以这个问题可以忽略。

综上所述,使用MD5值作为文件名的一部分,可以充分利用浏览器缓存,实现只有当文件内容变化时,缓存才失效的原则。

网易云大礼包:https://www.163yun.com/gift

本文来自网易实践者社区,经作者梁枫授权发布