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

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

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


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

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

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

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


当前回答

我建议执行以下流程:

无论何时部署你的CSS和JavaScript文件。类似于:screen.1233.css(如果您使用版本控制系统,该数字可以是您的SVN版本) 缩小它们以优化加载时间

其他回答

静态文件的简单解决方案(仅用于开发目的),使用脚本标记注入将随机版本号添加到脚本URI

<script>
    var script = document.createElement('script');
    script.src = "js/app.js?v=" + Math.random();
    document.getElementsByTagName('head')[0].appendChild(script);
</script>

对现有答案的小改进……

使用随机数或会话id将导致它在每个请求时重新加载。理想情况下,我们可能只需要在任何JavaScript或CSS文件中完成一些代码更改时才需要更改。

当使用一个公共JSP文件作为许多其他JSP和JavaScript文件的模板时,请在一个公共JSP文件中添加以下内容

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<c:set var = "version" scope = "application" value = "1.0.0" />

现在在JavaScript文件包含的所有位置使用上述变量,如下所示。

<script src='<spring:url value="/js/myChangedFile.js?version=${version}"/>'></script>

优点:

这种方法将帮助您仅在一个位置更改版本号。

维护一个适当的版本号(通常是构建/发布号)将帮助您检查/验证您的代码更改是否被正确部署(从浏览器的开发人员控制台)。

另一个有用的建议:

如果使用Chrome浏览器,可以在打开“开发工具”时禁用缓存。 在Chrome浏览器中,点击F12→F1,滚动到设置→首选项→网络→*禁用缓存(当DevTools打开时)

你可以把?foo=1234放在CSS / JavaScript导入的末尾,把1234改成你喜欢的任何值。看一下Stack Overflow HTML源代码的例子。

这里的意思是?无论如何,参数都会在请求中被丢弃/忽略,您可以在推出新版本时更改这个数字。


注意:关于这究竟如何影响缓存存在一些争论。我认为它的一般要点是GET请求,无论是否带参数都应该是可缓存的,因此上面的解决方案应该是有效的。

然而,这取决于web服务器和用户使用的浏览器,因为它可以直接要求一个新的版本。

这个解决方案是用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。基本上任何不是动态生成的东西。

这里的许多答案主张向URL添加时间戳。除非直接修改生产文件,否则文件的时间戳不太可能反映文件更改的时间。在大多数情况下,这将导致URL比文件本身更频繁地更改。这就是为什么你应该使用文件内容的快速散列,如levik和其他人建议的MD5。

请记住,该值应该在构建或运行时计算一次,而不是在每次请求文件时计算一次。

例如,下面是一个简单的bash脚本,它从标准输入读取文件名列表,并将包含散列的JSON文件写入标准输出:

#!/bin/bash
# Create a JSON map from filenames to MD5 hashes
# Run as hashes.sh < inputfile.list > outputfile.json

echo "{"
delim=""
while read l; do
    echo "$delim\"$l\": \"`md5 -q $l`\""
    delim=","
done
echo "}"

然后,该文件可以在服务器启动时加载并引用,而不是读取文件系统。