我注意到一些浏览器(特别是Firefox和Opera)非常热衷于使用.css和.js文件的缓存副本,甚至在浏览器会话之间。当您更新其中一个文件时,这会导致一个问题,但用户的浏览器会继续使用缓存的副本。
当文件发生更改时,强迫用户浏览器重新加载文件的最优雅的方法是什么?
理想情况下,该解决方案不会强制浏览器在每次访问页面时重新加载文件。
我发现John Millikin和da5id的建议很有用。这有一个专门的术语:自动版本控制。
我在下面发布了一个新的答案,这是我最初的解决方案和约翰的建议的结合。
SCdF建议的另一个想法是将伪查询字符串附加到文件中。(一些自动使用时间戳作为伪查询字符串的Python代码是由pi..提交的)
然而,关于浏览器是否缓存带有查询字符串的文件还存在一些讨论。(请记住,我们希望浏览器缓存该文件并在以后的访问中使用它。我们只希望它在文件更改时再次获取该文件。)
对于在开发和测试时遇到此问题的开发人员:
简单地删除缓存。
“保持缓存与文件一致”..太麻烦了。
一般来说,我不介意加载更多的文件——甚至在大多数项目中加载没有改变的文件——实际上是无关紧要的。在开发应用程序时,我们主要从磁盘加载,在localhost:port上加载,因此网络流量的增加不是一个棘手的问题。
大多数小项目只是玩玩而已——它们从来没有真正投入生产。所以对他们来说,你不需要更多的东西……
因此,如果你使用Chrome DevTools,你可以遵循如下图所示的禁用缓存方法:
如果你有Firefox缓存问题:
只在开发过程中这样做。你还需要一种机制来强制重新加载用于生产,因为如果你频繁地更新你的应用程序,你的用户将使用旧的缓存失效的模块,而你没有提供一个专门的缓存同步机制,就像上面的答案中描述的那样。
是的,这个信息已经在以前的答案中,但我仍然需要做谷歌搜索才能找到它。
对现有答案的小改进……
使用随机数或会话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打开时)
只需使用服务器端代码添加文件的日期…这样,它将被缓存,只有当文件更改时才会重新加载。
在ASP。NET:
<link rel="stylesheet" href="~/css/custom.css?d=@(System.Text.RegularExpressions.Regex.Replace(File.GetLastWriteTime(Server.MapPath("~/css/custom.css")).ToString(),"[^0-9]", ""))" />
<script type="text/javascript" src="~/js/custom.js?d=@(System.Text.RegularExpressions.Regex.Replace(File.GetLastWriteTime(Server.MapPath("~/js/custom.js")).ToString(),"[^0-9]", ""))"></script>
这可以简化为:
<script src="<%= Page.ResolveClientUrlUnique("~/js/custom.js") %>" type="text/javascript"></script>
通过在项目中添加扩展方法来扩展Page:
public static class Extension_Methods
{
public static string ResolveClientUrlUnique(this System.Web.UI.Page oPg, string sRelPath)
{
string sFilePath = oPg.Server.MapPath(sRelPath);
string sLastDate = System.IO.File.GetLastWriteTime(sFilePath).ToString();
string sDateHashed = System.Text.RegularExpressions.Regex.Replace(sLastDate, "[^0-9]", "");
return oPg.ResolveClientUrl(sRelPath) + "?d=" + sDateHashed;
}
}
这个解决方案是用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。基本上任何不是动态生成的东西。