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

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

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


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

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

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

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


当前回答

您可以简单地添加一些随机数与CSS和JavaScript URL

example.css?randomNo = Math.random()

其他回答

我在为我的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");
}

好吧,我已经通过改变JavaScript文件版本每次页面加载通过添加一个随机数的JavaScript文件版本,如下所示:

// Add it to the top of the page
<?php
    srand();
    $random_number = rand();
?>

然后将随机数应用到JavaScript版本,如下所示:

<script src="file.js?version=<?php echo $random_number;?>"></script>

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

而不是:

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

你可以使用:

$.ajaxSetup({
  cache: false
});

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

对于开发:使用浏览器设置:例如,Chrome网络选项卡有一个禁用缓存选项。

对于生产:使用服务器端呈现框架或纯JavaScript代码将唯一的查询参数附加到请求(例如,q?Date.now())。

// Pure JavaScript unique query parameter generation
//
//=== myfile.js

function hello() { console.log('hello') };

//=== end of file

<script type="text/javascript">
    document.write('<script type="text/javascript" src="myfile.js?q=' + Date.now() + '">
    // document.write is considered bad practice!
    // We can't use hello() yet
</script>')

<script type="text/javascript">
    hello();
</script>

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

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


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

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