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

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

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


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

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

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

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


当前回答

最简单的方法是利用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以外的东西,则根据语言的不同会有一些变化,但几乎所有语言都有打印文件内容的方法。将它放在正确的位置(在部分中),这样就不必依赖浏览器了。

其他回答

最简单的方法是利用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以外的东西,则根据语言的不同会有一些变化,但几乎所有语言都有打印文件内容的方法。将它放在正确的位置(在部分中),这样就不必依赖浏览器了。

如果使用jQuery,有一个叫做缓存的选项,它会附加一个随机数。

我知道这不是一个完整的答案,但它可能会节省你一些时间。

这个解决方案是用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。基本上任何不是动态生成的东西。

我这样做的方法是简单地有链接元素到服务器端包含:

<!--#include virtual="/includes/css-element.txt"-->

css-element.txt的内容在哪里

<link rel="stylesheet" href="mycss.css"/>

如果你想链接到my-new-css。css,你只需要改变include。

假设你有一个文件可用:

/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".