我注意到一些浏览器(特别是Firefox和Opera)非常热衷于使用.css和.js文件的缓存副本,甚至在浏览器会话之间。当您更新其中一个文件时,这会导致一个问题,但用户的浏览器会继续使用缓存的副本。

当文件发生更改时,强迫用户浏览器重新加载文件的最优雅的方法是什么?

理想情况下,该解决方案不会强制浏览器在每次访问页面时重新加载文件。


我发现John Millikin和da5id的建议很有用。这有一个专门的术语:自动版本控制。

我在下面发布了一个新的答案,这是我最初的解决方案和约翰的建议的结合。

SCdF建议的另一个想法是将伪查询字符串附加到文件中。(一些自动使用时间戳作为伪查询字符串的Python代码是由pi..提交的)

然而,关于浏览器是否缓存带有查询字符串的文件还存在一些讨论。(请记住,我们希望浏览器缓存该文件并在以后的访问中使用它。我们只希望它在文件更改时再次获取该文件。)


当前回答

仅在纯JavaScript的本地开发中禁用script.js的缓存。

它会注入一个随机的script.js?Wizardry =1231234并阻塞常规script.js:

<script type="text/javascript">
  if(document.location.href.indexOf('localhost') !== -1) {
    const scr = document.createElement('script');
    document.setAttribute('type', 'text/javascript');
    document.setAttribute('src', 'scripts.js' + '?wizardry=' + Math.random());
    document.head.appendChild(scr);
    document.write('<script type="application/x-suppress">'); // prevent next script(from other SO answer)
  }
</script>

<script type="text/javascript" src="scripts.js">

其他回答

不要使用foo.css?version=1!

浏览器不应该缓存带有GET变量的url。据http://www.thinkvitamin.com/features/webapps/serving-javascript-fast网站报道,尽管ie和Firefox会忽略这一点,但Opera和Safari不会!相反,使用foo.v1234.css,并使用重写规则去除版本号。

如果你正在使用Git和PHP,你可以在每次Git存储库中有变化时从缓存中重新加载脚本,使用以下代码:

exec('git rev-parse --verify HEAD 2> /dev/null', $gitLog);
echo '  <script src="/path/to/script.js"?v='.$gitLog[0].'></script>'.PHP_EOL;

我建议您使用实际CSS文件的MD5散列,而不是手动更改版本。

URL应该是这样的

http://mysite.com/css/[md5_hash_here]/style.css

您仍然可以使用重写规则来去除散列,但优点是现在您可以将缓存策略设置为“永远缓存”,因为如果URL相同,这意味着文件没有改变。

然后,您可以编写一个简单的shell脚本来计算文件的散列并更新标记(您可能希望将其移动到一个单独的文件中进行包含)。

只要在CSS每次更改时运行该脚本就可以了。浏览器只会在文件被修改时重新加载。如果你做了一个编辑,然后撤销它,为了让你的访问者不重新下载,你不需要确定需要返回到哪个版本。

有趣的文章。阅读了这里所有的答案,并结合我从未遇到过任何“伪”查询字符串问题的事实(我不确定为什么每个人都不愿意使用这个),我猜解决方案(它消除了Apache重写规则的需要,因为在接受的答案)是计算CSS文件内容的短哈希(而不是文件日期时间)作为伪查询字符串。

这将导致以下结果:

<link rel="stylesheet" href="/css/base.css?[hash-here]" type="text/css" />

当然,datetime解决方案也可以在编辑CSS文件的情况下完成工作,但我认为这是关于CSS文件的内容,而不是关于文件datetime,所以为什么要把这些混为一谈呢?

只需在你想要硬重载(强制浏览器重载缓存的CSS和JavaScript文件)的地方添加以下代码:

$(window).load(function() {
    location.reload(true);
});

在.load中执行此操作,这样它就不会像循环一样刷新。