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

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

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


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

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

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

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


当前回答

更改文件名也可以。但这通常不是最简单的解决方案。

正如你所注意到的,HTTP缓存控制标头为“no-cache”并不总是有效。HTTP 1.1规范为用户代理提供了决定是否请求新副本的回旋余地。(如果只看指令的名称,这是不直观的。去阅读实际的HTTP 1.1缓存规范…这在上下文中更有意义。)

简而言之,如果你想要严格的缓存控制使用

Cache-Control: no-cache, no-store, must-revalidate

在响应头中。

其他回答

感谢Kip的完美解决方案!

我对它进行了扩展,将其用作Zend_view_Helper。因为我的客户端在虚拟主机上运行他的页面,我也为此扩展了它。

/**
 * Extend filepath with timestamp to force browser to
 * automatically refresh them if they are updated
 *
 * This is based on Kip's version, but now
 * also works on virtual hosts
 * @link http://stackoverflow.com/questions/118884/what-is-an-elegant-way-to-force-browsers-to-reload-cached-css-js-files
 *
 * Usage:
 * - extend your .htaccess file with
 * # Route for My_View_Helper_AutoRefreshRewriter
 * # which extends files with there timestamp so if these
 * # are updated a automatic refresh should occur
 * # RewriteRule ^(.*)\.[^.][\d]+\.(css|js)$ $1.$2 [L]
 * - then use it in your view script like
 * $this->headLink()->appendStylesheet( $this->autoRefreshRewriter($this->cssPath . 'default.css'));
 *
 */
class My_View_Helper_AutoRefreshRewriter extends Zend_View_Helper_Abstract {

    public function autoRefreshRewriter($filePath) {

        if (strpos($filePath, '/') !== 0) {

            // Path has no leading '/'
            return $filePath;
        } elseif (file_exists($_SERVER['DOCUMENT_ROOT'] . $filePath)) {

            // File exists under normal path
            // so build path based on this
            $mtime = filemtime($_SERVER['DOCUMENT_ROOT'] . $filePath);
            return preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $filePath);
        } else {

            // Fetch directory of index.php file (file from all others are included)
            // and get only the directory
            $indexFilePath = dirname(current(get_included_files()));

            // Check if file exist relativ to index file
            if (file_exists($indexFilePath . $filePath)) {

                // Get timestamp based on this relativ path
                $mtime = filemtime($indexFilePath . $filePath);

                // Write generated timestamp to path
                // but use old path not the relativ one
                return preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $filePath);
            } else {
                return $filePath;
            }
        }
    }
}

有趣的文章。阅读了这里所有的答案,并结合我从未遇到过任何“伪”查询字符串问题的事实(我不确定为什么每个人都不愿意使用这个),我猜解决方案(它消除了Apache重写规则的需要,因为在接受的答案)是计算CSS文件内容的短哈希(而不是文件日期时间)作为伪查询字符串。

这将导致以下结果:

<link rel="stylesheet" href="/css/base.css?[hash-here]" type="text/css" />

当然,datetime解决方案也可以在编辑CSS文件的情况下完成工作,但我认为这是关于CSS文件的内容,而不是关于文件datetime,所以为什么要把这些混为一谈呢?

location.reload(true)

或者使用检查器中的“网络”([CTRL] + [I]),点击“禁用缓存”,点击垃圾图标,点击“加载”/“获取”

最简单的方法是利用PHP文件读取功能。只需让PHP将文件内容回显到标记中。

<?php
//Replace the 'style.css' with the link to the stylesheet.
echo "<style type='text/css'>".file_get_contents('style.css')."</style>";
?>

如果您使用的是PHP以外的东西,则根据语言的不同会有一些变化,但几乎所有语言都有打印文件内容的方法。将它放在正确的位置(在部分中),这样就不必依赖浏览器了。

我听说这叫做“自动版本控制”。最常见的方法是在URL中包含静态文件的修改时间,并使用重写处理程序或URL配置将其剥离:

参见:

Django中的自动资产版本控制 自动版本你的CSS和JavaScript文件