我注意到一些浏览器(特别是Firefox和Opera)非常热衷于使用.css和.js文件的缓存副本,甚至在浏览器会话之间。当您更新其中一个文件时,这会导致一个问题,但用户的浏览器会继续使用缓存的副本。
当文件发生更改时,强迫用户浏览器重新加载文件的最优雅的方法是什么?
理想情况下,该解决方案不会强制浏览器在每次访问页面时重新加载文件。
我发现John Millikin和da5id的建议很有用。这有一个专门的术语:自动版本控制。
我在下面发布了一个新的答案,这是我最初的解决方案和约翰的建议的结合。
SCdF建议的另一个想法是将伪查询字符串附加到文件中。(一些自动使用时间戳作为伪查询字符串的Python代码是由pi..提交的)
然而,关于浏览器是否缓存带有查询字符串的文件还存在一些讨论。(请记住,我们希望浏览器缓存该文件并在以后的访问中使用它。我们只希望它在文件更改时再次获取该文件。)
ASP。NET我建议以下解决方案与高级选项(调试/发布模式,版本):
以这种方式包含JavaScript或CSS文件:
<script type="text/javascript" src="Scripts/exampleScript<%=Global.JsPostfix%>" />
<link rel="stylesheet" type="text/css" href="Css/exampleCss<%=Global.CssPostfix%>" />
全球。JsPostfix和Global。在Global.asax中,CssPostfix的计算方法如下:
protected void Application_Start(object sender, EventArgs e)
{
...
string jsVersion = ConfigurationManager.AppSettings["JsVersion"];
bool updateEveryAppStart = Convert.ToBoolean(ConfigurationManager.AppSettings["UpdateJsEveryAppStart"]);
int buildNumber = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.Revision;
JsPostfix = "";
#if !DEBUG
JsPostfix += ".min";
#endif
JsPostfix += ".js?" + jsVersion + "_" + buildNumber;
if (updateEveryAppStart)
{
Random rand = new Random();
JsPosfix += "_" + rand.Next();
}
...
}
我建议您使用实际CSS文件的MD5散列,而不是手动更改版本。
URL应该是这样的
http://mysite.com/css/[md5_hash_here]/style.css
您仍然可以使用重写规则来去除散列,但优点是现在您可以将缓存策略设置为“永远缓存”,因为如果URL相同,这意味着文件没有改变。
然后,您可以编写一个简单的shell脚本来计算文件的散列并更新标记(您可能希望将其移动到一个单独的文件中进行包含)。
只要在CSS每次更改时运行该脚本就可以了。浏览器只会在文件被修改时重新加载。如果你做了一个编辑,然后撤销它,为了让你的访问者不重新下载,你不需要确定需要返回到哪个版本。
假设你有一个文件可用:
/styles/screen.css
你可以在URI上附加一个包含版本信息的查询参数,例如:
/styles/screen.css?v=1234
或者你可以在前面加上版本信息,例如:
/v/1234/styles/screen.css
恕我直言,第二种方法更适合CSS文件,因为它们可以使用相对url引用图像,这意味着如果你指定一个背景图像,像这样:
body {
background-image: url('images/happy.gif');
}
它的URL实际上是:
/v/1234/styles/images/happy.gif
这意味着如果您更新了使用的版本号,服务器将将其视为新资源,而不是使用缓存的版本。如果您的版本号基于Subversion、CVS等版本,这意味着CSS文件中引用的图像的更改将被注意到。第一种方案并不能保证这一点,即URL images/happy.gif相对于/styles/screen.css?V =1235是/styles/images/happy.gif,它不包含任何版本信息。
I have implemented a caching solution using this technique with Java servlets and simply handle requests to /v/* with a servlet that delegates to the underlying resource (i.e. /styles/screen.css). In development mode I set caching headers that tell the client to always check the freshness of the resource with the server (this typically results in a 304 if you delegate to Tomcat's DefaultServlet and the .css, .js, etc. file hasn't changed) while in deployment mode I set headers that say "cache forever".