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

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

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


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

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

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

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


当前回答

"Another idea which was suggested by SCdF would be to append a bogus query string to the file. (Some Python code to automatically use the timestamp as a bogus query string was submitted by pi.) However, there is some discussion as to whether or not the browser would cache a file with a query string. (Remember, we want the browser to cache the file and use it on future visits. We only want it to fetch the file again when it has changed.) Since it is not clear what happens with a bogus query string, I am not accepting that answer."

<link rel="stylesheet" href="file.css?<?=hash_hmac('sha1', session_id(), md5_file("file.css")); ?>" />

哈希文件意味着当文件发生变化时,查询字符串也会发生变化。如果没有,它将保持不变。每个会话也强制重新加载。

此外,您还可以使用重写使浏览器认为它是一个新的URI。

其他回答

JavaScript文件的另一种方法是使用jQuery $。与$. getScript结合使用。ajaxSetup选项缓存:false。

而不是:

<script src="scripts/app.js"></script>

你可以使用:

$.ajaxSetup({
  cache: false
});

$.getScript('scripts/app.js'); // GET scripts/app.js?_1391722802668

这个解决方案是用PHP编写的,但是应该很容易适应其他语言。

原始的.htaccess正则表达式可能会导致json-1.3.js等文件出现问题。解决方案是只有在结尾恰好有10位数字时才重写。(因为10位数字涵盖了从2001年9月9日到2286年11月20日的所有时间戳。)

首先,我们在.htaccess中使用下面的重写规则:

RewriteEngine on
RewriteRule ^(.*)\.[\d]{10}\.(css|js)$ $1.$2 [L]

现在,我们编写以下PHP函数:

/**
 *  Given a file, i.e. /css/base.css, replaces it with a string containing the
 *  file's mtime, i.e. /css/base.1221534296.css.
 *
 *  @param $file  The file to be loaded.  Must be an absolute path (i.e.
 *                starting with slash).
 */
function auto_version($file)
{
  if(strpos($file, '/') !== 0 || !file_exists($_SERVER['DOCUMENT_ROOT'] . $file))
    return $file;

  $mtime = filemtime($_SERVER['DOCUMENT_ROOT'] . $file);
  return preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $file);
}

现在,无论你在哪里包含你的CSS,从下面更改它:

<link rel="stylesheet" href="/css/base.css" type="text/css" />

:

<link rel="stylesheet" href="<?php echo auto_version('/css/base.css'); ?>" type="text/css" />

这样,您就不必再次修改链接标签,用户将始终看到最新的CSS。浏览器将能够缓存CSS文件,但当您对CSS进行任何更改时,浏览器将看到这是一个新的URL,因此它不会使用缓存的副本。

这也适用于图像、favicons和JavaScript。基本上任何不是动态生成的东西。

在ASP。NET Core你可以通过添加'asp-追加-版本'来实现:

<link rel="stylesheet" href="~/css/xxx.css" asp-append-version="true" />

 <script src="~/js/xxx.js" asp-append-version="true"></script>

它将生成HTML:

<link rel="stylesheet" href="/css/xxx.css?v=rwgRWCjxemznsx7wgNx5PbMO1EictA4Dd0SjiW0S90g" />

每当您更新文件时,框架将生成一个新的版本号。

更改文件名也可以。但这通常不是最简单的解决方案。

正如你所注意到的,HTTP缓存控制标头为“no-cache”并不总是有效。HTTP 1.1规范为用户代理提供了决定是否请求新副本的回旋余地。(如果只看指令的名称,这是不直观的。去阅读实际的HTTP 1.1缓存规范…这在上下文中更有意义。)

简而言之,如果你想要严格的缓存控制使用

Cache-Control: no-cache, no-store, must-revalidate

在响应头中。

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

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