Enjoy software architecture and programming

27Sep 2019

浅谈网站前端 CSS(JS) 文件缓存问题

968 words - 5 mins

网站中 CSS 和 JS 文件的缓存问题,一直以来我没有弄清楚其原理和方法,导致在应用时疑问重重。经过近段时间,查询相关资料,结合深入思考和相关应用,对于 CSS 和 JS 的前端缓存问题,形成自己的初步见解。本文就此问题展开简单论述。

一般来说,为了提高网站的访问速度,让用户的浏览体验更快,通常会将前端的 CSS 和 JS 文件缓存在浏览器端。具体操作原理就是,在网站服务器发送页面的时候设置页面的生命周期,然后浏览器根据该生命周期对页面进行缓存。这也是为什么我们首次打开某网站页面比较慢,第二次打开就快的原因。同时,这样做还有一个好处就是,一般几个小时的缓存,就能缓解很大的服务器压力,因为页面中众多的静态小文件对服务器的网络 I/O 是个巨大的消耗。并且,对于浏览者来讲,本地页面晚更新几个小时,出现的问题可能也不大。

事物总有它的两面性,这样也带来一个问题,当页面发生了更新,该页面链接的 JS 和 CSS 文件却被浏览器缓存下来,而这些文件可能在浏览器存在相当长的时间。这样页面与 CSS 和 JS 错配,导致出现各种异常问题。

对于该缓存问题,这里有一种简单并且行之有效的解决方案。正如前文所述,浏览器缓存页面时,该页面链接的 JS 和 CSS 文件也被浏览器缓存下来,例如本站的 HTML 头部中 CSS 文件:

<link rel="stylesheet" href="/themes/Kueen/css/style.css?v=brWJ21jh9wN4eNNXeYWue1R9HqD889h1djc-szWVHnU" type="text/css"/> 

可能你已注意到上面的 CSS 文件链接都加上了一个版本号,格式如:

<link rel="stylesheet" type="text/css" href="{CSS文件连接地址}?v=XXXXXXXX"/>

当页面对应的 CSS 或 JS 发生更新,如果文件链接未发生改变,由于浏览器缓存的时候是以 URL 作为存储单位,那么页面使用的 CSS 或 JS 仍然是浏览器之前缓存的文件,而不是最新文件。但也是因为浏览器是以 URL 作为存储单位进行缓存,当页面的 CSS 或 JS 文件更新时,我们只需要更改链接中的版本号,浏览器由于没有新的 URL 对应的缓存文件,就会重新下载该文件。

同样,这里也会引来另外一个问题,就是版本号如何生成?网上通常做法就是发布的时候,版本号改为发布日期或者时间戳,这对于一两个文件更新时,版本号更新没什么问题,但对于 CSS 和 JS 较多的网站,发布时版本号的维护成本是比较高的,也存在版本号忘记更新的可能。

对于上述问题,我觉得有个更为妥当的方式,就是版本号使用文件的 Md5,当文件未更新时 Md5 保持不变,而文件更新时文件 Md5 会发生变化,这就将所有文件版本号统一进行生成,不管是否更新,生成规则是一致的,减少了维护成本。同时,我也推荐 ASP.NET 中 Razor 标记生成版本号,原理与按文件 Md5 生成版本号类似。ASP.NET 中 Razor 标记用法如下:

<link rel="stylesheet" href="~/themes/@theme/css/custom.css" type="text/css" media="all" asp-append-version="true" /> 
<script type="text/javascript" src="~/themes/@theme/js/jquery.js" asp-append-version="true"></script>

关键在于后面的:asp-append-version="true",意思是开启附件版本号。