如何强制浏览器重新加载缓存的CSS和JavaScript文件

2022-08-29 22:07:38

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

强制用户浏览器在文件更改时重新加载文件的最优雅方法是什么?

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


我发现John Millikinda5id的建议很有用。事实证明,有一个术语:自动版本控制

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

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

但是,对于浏览器是否会使用查询字符串缓存文件,存在一些讨论。(请记住,我们希望浏览器缓存文件并在将来的访问中使用它。我们只希望它在文件更改时再次获取文件。


答案 1

这个解决方案是用PHP编写的,但它应该很容易适应其他语言。

原始正则表达式可能会导致 ..解决方案是仅在末尾正好有 10 位数字时才重写。(因为 10 位数字涵盖从 9/9/2001 到 11/20/2286 的所有时间戳。.htaccessjson-1.3.js

首先,我们在 .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。基本上任何不是动态生成的。


答案 2

简单的客户端技术

一般来说,缓存是好的...因此,有几种技术,具体取决于您在开发网站时是否要自己解决问题,或者是否要尝试在生产环境中控制缓存。

您网站的一般访问者将不会获得与开发网站时相同的体验。由于普通访问者访问该网站的频率较低(除非您是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 标头,但是我上面写的小技术可能是一种更简单的方法。由于缓存很好,因此您通常希望将该日期设置为很远的未来(“远未来过期标头”),并使用上述技术强制进行更改。

如果你对更多关于HTTP的信息或这些请求是如何发出的感兴趣,一本好书是Steve Souders的“高性能网站”。这是对这个主题的一个很好的介绍。