我注意到一些浏览器(特别是Firefox和Opera)非常热衷于使用.css和.js文件的缓存副本,甚至在浏览器会话之间。当您更新其中一个文件时,这会导致一个问题,但用户的浏览器会继续使用缓存的副本。
当文件发生更改时,强迫用户浏览器重新加载文件的最优雅的方法是什么?
理想情况下,该解决方案不会强制浏览器在每次访问页面时重新加载文件。
我发现John Millikin和da5id的建议很有用。这有一个专门的术语:自动版本控制。
我在下面发布了一个新的答案,这是我最初的解决方案和约翰的建议的结合。
SCdF建议的另一个想法是将伪查询字符串附加到文件中。(一些自动使用时间戳作为伪查询字符串的Python代码是由pi..提交的)
然而,关于浏览器是否缓存带有查询字符串的文件还存在一些讨论。(请记住,我们希望浏览器缓存该文件并在以后的访问中使用它。我们只希望它在文件更改时再次获取该文件。)
我建议您使用实际CSS文件的MD5散列,而不是手动更改版本。
URL应该是这样的
http://mysite.com/css/[md5_hash_here]/style.css
您仍然可以使用重写规则来去除散列,但优点是现在您可以将缓存策略设置为“永远缓存”,因为如果URL相同,这意味着文件没有改变。
然后,您可以编写一个简单的shell脚本来计算文件的散列并更新标记(您可能希望将其移动到一个单独的文件中进行包含)。
只要在CSS每次更改时运行该脚本就可以了。浏览器只会在文件被修改时重新加载。如果你做了一个编辑,然后撤销它,为了让你的访问者不重新下载,你不需要确定需要返回到哪个版本。
"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。
只需使用服务器端代码添加文件的日期…这样,它将被缓存,只有当文件更改时才会重新加载。
在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;
}
}
我在为我的SPA寻找解决方案时遇到了这个问题,它只有一个列出所有必要文件的index.html文件。虽然我得到了一些帮助我的线索,但我找不到一个快速而简单的解决方案。
最后,我编写了一个快速页面(包括所有代码)来自动版本HTML/JavaScript index.html文件,作为发布过程的一部分。它工作完美,只更新新文件根据日期最后修改。
你可以在Autoversion your SPA index.html上看到我的文章。还有一个独立的Windows应用程序。
代码的核心是:
private void ParseIndex(string inFile, string addPath, string outFile)
{
string path = Path.GetDirectoryName(inFile);
HtmlAgilityPack.HtmlDocument document = new HtmlAgilityPack.HtmlDocument();
document.Load(inFile);
foreach (HtmlNode link in document.DocumentNode.Descendants("script"))
{
if (link.Attributes["src"]!=null)
{
resetQueryString(path, addPath, link, "src");
}
}
foreach (HtmlNode link in document.DocumentNode.Descendants("link"))
{
if (link.Attributes["href"] != null && link.Attributes["type"] != null)
{
if (link.Attributes["type"].Value == "text/css" || link.Attributes["type"].Value == "text/html")
{
resetQueryString(path, addPath, link, "href");
}
}
}
document.Save(outFile);
MessageBox.Show("Your file has been processed.", "Autoversion complete");
}
private void resetQueryString(string path, string addPath, HtmlNode link, string attrType)
{
string currFileName = link.Attributes[attrType].Value;
string uripath = currFileName;
if (currFileName.Contains('?'))
uripath = currFileName.Substring(0, currFileName.IndexOf('?'));
string baseFile = Path.Combine(path, uripath);
if (!File.Exists(baseFile))
baseFile = Path.Combine(addPath, uripath);
if (!File.Exists(baseFile))
return;
DateTime lastModified = System.IO.File.GetLastWriteTime(baseFile);
link.Attributes[attrType].Value = uripath + "?v=" + lastModified.ToString("yyyyMMddhhmm");
}