如何强制浏览器重新加载缓存的CSS / JS文件?

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

问题是:当文件发生变化时,强制用户浏览器重新加载文件的最优雅方式是什么?

理想情况下,解决方案不会强制浏览器在每次访问页面时重新加载文件。 我会发布自己的解决方案作为答案,但我很好奇,如果有人有更好的解决方案,我会让你的选票决定。

更新:

在经过一段时间的讨论后,我发现John Millikinda5id的建议很有用。 事实证明,这是一个术语: 自动版本控制

我在下面发布了一个新的答案,这是我原来的解决方案和John的建议的结合。

SCdF提出的另一个想法是将伪造的查询字符串附加到文件中。 (某些Python代码自动使用时间戳作为伪造查询字符串,由pi提交)。 但是,有一些关于浏览器是否会用查询字符串缓存文件的讨论。 (请记住,我们希望浏览器缓存文件并在将来的访问中使用它,我们只希望它在文件更改后再次获取文件。)

由于不清楚假冒查询字符串会发生什么情况,因此我不接受该答案。


更新:重写包含John Millikinda5id的建议。 这个解决方案是用PHP编写的,但应该很容易适应其他语言。

更新2:纳入Nick Johnson的评论,原始的.htaccess正则表达式可能导致像json-1.3.js这样的文件出现问题。 解决方案是只在最后有10位数字时才重写。 (因为10位数字涵盖了从9/9/2001到11/20/2286的所有时间戳。)

首先,我们在.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,因此它不会使用缓存副本。

这也可以与图片,网站图标和JavaScript一起使用。 基本上任何不是动态生成的。


简单的客户端技术

一般来说,缓存是好的。所以有几种技术,这取决于您在开发网站时是否为自己解决问题,或者您是否试图控制生产环境中的缓存。

您网站的一般访问者将无法获得与开发网站时相同的体验。 由于普通用户访问网站的频率较低(可能每月只有几次,除非您是Google或hi5 Networks),因此他们不太可能将您的文件放入缓存中,这可能就足够了。 如果您想要在浏览器中强制使用新版本,则可以随时向请求中添加查询字符串,并在进行重大更改时提升版本号:

<script src="/myJavascript.js?version=4"></script>

这将确保每个人都获得新文件。 它的工作原理是浏览器查看文件的URL以确定它是否在缓存中有副本。 如果您的服务器没有设置为对查询字符串执行任何操作,它将被忽略,但该名称在浏览器中看起来像一个新文件。

另一方面,如果您正在开发一个网站,则每次保存对开发版本的更改时都不想更改版本号。 这将是乏味的。

所以,当你开发你的网站时,一个好的技巧是自动生成一个查询字符串参数:

<!-- Development version: -->
<script>document.write('<script src="/myJavascript.js?dev=' + Math.floor(Math.random() * 100) + '"></script>');</script>

向请求添加查询字符串是版本化资源的好方法,但对于简单的网站来说,这可能是不必要的。 请记住,缓存是一件好事。

同样值得注意的是,浏览器并不一定要将文件保存在缓存中。 浏览器对这类事情有相应的策略,而且他们通常是按照HTTP规范中规定的规则来玩的。 当浏览器向服务器发送请求时,响应的一部分是EXPIRES标头..这是一个告诉浏览器它应该保存在缓存中的时间的日期。 下一次浏览器遇到同一个文件的请求时,它会看到它在缓存中有一个副本,并查看EXPIRES日期以决定是否应该使用它。

所以不管你信不信,它实际上是你的服务器,使得浏览器缓存如此持久。 你可以调整你的服务器设置并更改EXPIRES头文件,但是我上面写的小技巧对于你来说可能是一种更简单的方法。 由于高速缓存是好的,你通常希望将这个日期设置到将来(“远期过期标题”),并使用上述技术来强制进行更改。

如果您对HTTP的更多信息感兴趣,或者对这些请求的处理方式感兴趣,那么Steve Souders提供的一本好书就是“高性能网站”。 这是一个非常好的主题介绍。


谷歌的mod_pagespeed插件将为你做自动版本控制。 这真的很光滑。

它在离开web服务器的路上解析HTML(与PHP,rails,python,静态HTML一起工作 - 任何东西)并重写链接到CSS,JS,图像文件,以便包含一个id代码。 它提供修改后的URL上的文件,并对它们使用非常长的缓存控制。 当文件发生变化时,它会自动更改URL,以便浏览器必须重新获取它们。 它基本上可以正常工作,不需要对代码进行任何更改。 它甚至可以在出路上缩减代码。

链接地址: http://www.djcxy.com/p/9503.html

上一篇: How to force browser to reload cached CSS/JS files?

下一篇: How to control web page caching, across all browsers?