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

1150
我注意到一些浏览器(尤其是Opera和Firefox)在使用缓存的.css.js文件时非常热衷,甚至在浏览器会话之间也是如此。当您更新其中一个文件但用户的浏览器继续使用缓存副本时,这会导致问题。
强制用户的浏览器在文件更改后重新加载文件的最优雅方法是什么?
理想情况下,解决方案不会在每次访问页面时都强制浏览器重新加载文件。

我发现John Millikinda5id的建议很有用。原来这个术语叫做自动版本控制

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

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

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


我在我的 .htaccess 文件中有这个设置,从未出现过缓存文件的问题:ExpiresActive On ExpiresDefault "modification" - Frank Conijn - Support Ukraine
2
我绝对同意将版本信息添加到文件的URL中是迄今为止最好的方法。 它可以始终为每个人正常工作。 但是,如果您没有使用它,并且只需要偶尔重新加载一个CSS或JS文件以在自己的浏览器中运行...只需在其自己的选项卡中打开它,然后按SHIFT-reload(或CTRL-F5)! 您可以通过在(隐藏的)iframe中加载文件,等待加载完成,然后调用 iframe.contentWindow.location.reload(true) 来有效地执行相同的操作。请参见 https://dev59.com/6XNA5IYBdhLWcg3wIqPr#22429796 的方法(4)-那是关于图片的,但同样适用。 - Doin
6
我很欣赏这个问题的提问方式,也很感谢提问者对问题进行了更新。它完整地描述了我应该在回答中期待什么。从现在开始,我会采用这种方法来提问。干杯! - rd22
2
参考:da5id's's deleted answer 是这样说的:“如果更新足够大/重要,我通常会更改文件的名称。”。 - Peter Mortensen
如果更改不是很频繁,我有一个建议。只需更改文件名并编辑源代码以包含新的文件名。然后浏览器就没有缓存文件可读取。 - SK-the-Learner
我写了一篇关于使用GitHub进行CDN、自动版本控制、自动更新等方面的方法的文章,这个方法真的很有效。https://dany1980.medium.com/cdn-e-aggiornamenti-automatici-degli-assets-che-restano-in-cache-d362a99f054d(这是从意大利语翻译过来的) - Daniele Rugginenti
58个回答

1
如果您不希望客户端缓存文件,这个解决方案似乎是最快实现的。如果您在footer.php中加载文件,则调整time()部分即可:
<script src="<?php echo get_template_directory_uri(); ?>/js/main.js?v=<?= time() ?>"></script>

1
一个简单的解决方案(仅用于开发目的),为静态文件添加随机版本号到脚本URI中,使用脚本标签注入。
<script>
    var script = document.createElement('script');
    script.src = "js/app.js?v=" + Math.random();
    document.getElementsByTagName('head')[0].appendChild(script);
</script>

1
许多答案都主张在URL中添加时间戳。除非您直接修改生产文件,否则文件的时间戳不太可能反映文件更改的时间。在大多数情况下,这将导致URL比文件本身更频繁地更改。这就是为什么应该像levik和其他人建议的那样使用文件内容的快速哈希值,例如MD5

请记住,该值应在构建或运行时计算一次,而不是每次请求文件时都计算一次。

例如,这里是一个简单的bash脚本,它从标准输入读取文件名列表,并将哈希写入JSON文件以输出到标准输出:

#!/bin/bash
# Create a JSON map from filenames to MD5 hashes
# Run as hashes.sh < inputfile.list > outputfile.json

echo "{"
delim=""
while read l; do
    echo "$delim\"$l\": \"`md5 -q $l`\""
    delim=","
done
echo "}"

这个文件可以在服务器启动时加载并参考,而不是读取文件系统。


1
如果您使用的是现代浏览器,您可以使用清单文件来通知浏览器哪些文件需要更新。这不需要头文件、URL中的版本等。
更多详情请参见: 使用应用程序缓存

1
另一个针对ASP.NET网站的建议,
  1. 为不同的静态文件设置不同的cache-control:max-age值。

  2. 对于CSS和JavaScript文件,由于服务器上修改这些文件的可能性很高,因此将cache-control:max-age值设置为1或2分钟或满足您需求的某个最小值。

  3. 对于图像,请将远日期设置为cache-control:max-age值,例如360天。

  4. 这样做时,当我们进行第一次请求时,所有静态内容均会通过200-OK响应下载到客户端机器上。

  5. 在随后的请求和两分钟之后,我们会看到关于CSS和JavaScript文件的304-Not Modified请求,这避免了我们进行CSS和JavaScript版本控制。

  6. 图像文件将不会被请求,因为它们将从缓存的内存中使用,直到��存过期。

  7. 通过使用下面的web.config配置,我们可以实现上述描述的行为:

    <system.webServer>
        <modules runAllManagedModulesForAllRequests="true"/>
        <staticContent>
            <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="00.00:01:00"/>
        </staticContent>
        <httpProtocol>
            <customHeaders>
                <add name="ETAG" value=""/>
            </customHeaders>
        </httpProtocol>
    </system.webServer>
    
    <location path="Images">
        <system.webServer>
            <staticContent>
                <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="180.00:00:00" />
            </staticContent>
        </system.webServer>
    </location>
    

1
“SCdF提出的另一个想法是向文件附加虚假的查询字符串。(pi提交了一些Python代码,自动使用时间戳作为虚假查询字符串。)但是,关于浏览器是否会缓存带有查询字符串的文件存在一些讨论。(记住,我们希望浏览器缓存文件并在将来的访问中使用它。只有在文件发生更改时,我们才希望它再次获取文件。)由于不清楚虚假查询字符串会发生什么,我不接受这个答案。”
<link rel="stylesheet" href="file.css?<?=hash_hmac('sha1', session_id(), md5_file("file.css")); ?>" />

对文件进行哈希处理意味着当文件发生更改时,查询字符串也会发生更改。如果没有更改,它将保持不变。每个会话也都会强制重新加载。

此外,您还可以使用重写来使浏览器认为这是一个新的URI(可选)。


1
在ASP.NET Core中,您可以通过添加'asp-append-version'来实现此目的:
<link rel="stylesheet" href="~/css/xxx.css" asp-append-version="true" />

 <script src="~/js/xxx.js" asp-append-version="true"></script>

它将生成HTML代码:

它将生成HTML:

<link rel="stylesheet" href="/css/xxx.css?v=rwgRWCjxemznsx7wgNx5PbMO1EictA4Dd0SjiW0S90g" />

每次更新文件时,框架都会生成一个新的版本号。


0

好的,我已经通过在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>

6
这个想法很不好。这意味着用户每次加载页面时都必须重新下载文件。缓存是一件好事——这个问题是关于如何在需要时利用缓存,而不是在不需要时利用缓存。 - Kip
是的,用户必须在每次页面加载时重新下载文件,因为某些嵌入在(iframe)下的JavaScript不会更新页面内容,除非用户按F5或手动重新加载页面。因此,每次新访问者访问网站时重新加载内容是最佳解决方案。 - Mizo Games

0
location.reload(true)

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


0

这是我的基于Bash脚本的缓存破坏解决方案:

  1. 假设你在index.html文件中引用了CSS和JavaScript文件
  2. index.html中为.js和.css添加一个时间戳作为参数(仅一次)
  3. 创建一个名为timestamp.txt的文件,其中包含上述时间戳。
  4. 对于任何对.css或.js文件的更新,只需运行以下.sh脚本

示例index.html条目,包含带有时间戳的.js和.css:

<link rel="stylesheet" href="bla_bla.css?v=my_timestamp">
<script src="scripts/bla_bla.js?v=my_timestamp"></script>

文件timestamp.txt应只包含相同的时间戳“my_timestamp”(稍后将由脚本搜索并替换)

最后,这是脚本(让我们称其为cache_buster.sh :D)

old_timestamp=$(cat timestamp.txt)
current_timestamp=$(date +%s)
sed -i -e "s/$old_timestamp/$current_timestamp/g" index.html
echo "$current_timestamp" >timestamp.txt

(Visual Studio Code 用户)您可以将此脚本放在钩子中,这样每次在您的工作区保存文件时都会调用它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接