【问题标题】:Cache busting via params通过参数清除缓存
【发布时间】:2012-03-30 08:43:19
【问题描述】:

我们希望在生产部署中缓存 bust,但不会浪费大量时间来找出一个这样做的系统。我的想法是在当前版本号的 css 和 js 文件的末尾应用一个参数:

<link rel="stylesheet" href="base_url.com/file.css?v=1.123"/>

两个问题:这会有效地破坏缓存吗?参数是否会导致浏览器从不缓存来自该 url 的响应,因为参数表明这是动态内容?

【问题讨论】:

    标签: javascript html css caching


    【解决方案1】:

    希望这能帮助你注入外部 JS 文件

    <script type="text/javascript"> 
    var cachebuster = Math.round(new Date().getTime() / 1000); 
    document.write('<scr'+'ipt type="text/javascript" src="external.js?cb=' +cachebuster+'"></scr' + 'ipt>');
    </script>
    

    来源 - Cachebuster code in JavaScript

    【讨论】:

    • 我不知道谁反对这个,这是一个完全可以接受的解决方案。也许值得补充的是,对于每个请求,这是绕过缓存的一种非常困难的方法,不仅是在更新资源和升级版本号时。对于您希望确保永远不会获取缓存数据作为响应的某些请求,它可能很有用。
    【解决方案2】:
     <script>
        var storedSrcElements = [
             "js/exampleFile.js",
             "js/sampleFile.js",
             "css/style.css"
              ];
    
        var head= document.getElementsByTagName('head')[0];
        var script;
        var link;
        var versionNumberNew = 4.6;
    
        for(i=0;i<storedSrcElements.length;i++){
         script= document.createElement('script');
         script.type= 'text/javascript';
         script.src= storedSrcElements[i] + "?" + versionNumberNew;
         head.appendChild(script);
        }     
    
    
         </script> 
    
    
           ### Change the version number  (versionNumberNew) when you want the new files to be loaded  ###
    

    【讨论】:

      【解决方案3】:

      另一种类似的方法是在提供文件时使用htaccess mod_rewrite 忽略部分路径。您从不缓存的索引页面引用了文件的最新路径。

      从开发的角度来看,它就像使用参数作为版本号一样简单,但它与文件名方法一样强大。

      使用路径中被忽略的部分作为版本号,服务器只是忽略它并提供未缓存的文件。

      1.2.3/css/styles.css 提供与css/styles.css 相同的文件,因为第一个目录已被 htaccess 文件剥离并忽略

      包括版本化文件

      <?php
        $version = "1.2.3";
      ?>
      
      <html>
        <head>
          <meta http-equiv="cache-control" content="max-age=0" />
          <meta http-equiv="cache-control" content="no-cache" />
          <meta http-equiv="expires" content="0" />
          <meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" />
          <meta http-equiv="pragma" content="no-cache" />
          <link rel="stylesheet" type="text/css" href="<?php echo $version ?>/css/styles.css">
        </head>
        <body>
          <script src="<?php echo $version ?>/js/main.js"></script>
        </body>
      </html>
      

      请注意,这种方法意味着您需要禁用索引页面的缓存 - Using <meta> tags to turn off caching in all browsers?

      .htaccess 文件

      RewriteEngine On
      
      # if you're requesting a file that exists, do nothing
      RewriteCond %{REQUEST_FILENAME} !-f 
      # likewise if a directory that exists, do nothing
      RewriteCond %{REQUEST_FILENAME} !-d 
      
      # otherwise, rewrite foo/bar/baz to bar/baz - ignore the first directory
      RewriteRule ^[^/]+/(.+)$ $1 [L] 
      

      您可以在任何允许 url 重写的服务器平台上采用相同的方法

      (改写条件改编自mod_rewrite - rewrite directory to query string except /#!/

      ...如果您的索引页面/站点入口点需要缓存清除,您可以随时use JavaSript 刷新它。

      【讨论】:

        【解决方案4】:
        <script type="text/javascript">
        // front end cache bust
        
        var cacheBust = ['js/StrUtil.js', 'js/protos.common.js', 'js/conf.js', 'bootstrap_ECP/js/init.js'];   
        for (i=0; i < cacheBust.length; i++){
             var el = document.createElement('script');
             el.src = cacheBust[i]+"?v=" + Math.random();
             document.getElementsByTagName('head')[0].appendChild(el);
        }
        </script> 
        

        【讨论】:

        • 在新版本的开发/测试期间,缓存可能会成为问题,因为浏览器、服务器甚至有时 3G 电信公司(如果您进行移动部署)都会缓存静态内容(例如 JS、 CSS,HTML,img)。您可以通过在 URL 中附加版本号、随机数或时间戳来解决此问题,例如:JSP:
        • 我认为这不会同步加载 JS 文件。
        【解决方案5】:

        找到两种技术的比较(查询字符串与文件名)here

        作为查询字符串的版本有两个问题。

        首先,它可能并不总是一个实现缓存的浏览器,我们需要通过它来破坏它。据说某些(可能较旧的)代理在缓存行为方面确实会忽略查询字符串。

        其次,在某些更复杂的部署场景中,您有多个前端和/或多个后端服务器,升级绝非瞬时完成。您需要能够同时提供旧版本和新版本的资产。例如,看看这对您在使用 Google App Engine 时有何影响。

        【讨论】:

          【解决方案6】:

          正如其他人所说,使用查询参数破坏缓存通常被认为是一个坏主意 (tm),并且已经存在很长时间了。最好在文件名中体现版本。 Html5 Boilerplate recommends against 使用查询字符串等。

          也就是说,在我看到的引用来源的建议中,似乎都从 Steve Souders 的 2008 article 中汲取了智慧。他的结论是基于当时代理人的行为,这些天他们可能相关,也可能不相关。不过,在没有更多最新信息的情况下,更改文件名是安全的选择。

          【讨论】:

            【解决方案7】:

            这在很大程度上取决于您希望缓存的健壮程度。例如,squid 代理服务器(可能还有其他)defaults to not caching URL 使用查询字符串提供服务——至少在撰写这篇文章时是这样。如果您不介意某些导致不必要的缓存未命中的用例,请继续使用查询参数。但是非常很容易设置一个基于文件名的缓存清除方案来避免这个问题。

            【讨论】:

            • Steve Souders 文章中引用的 squid 代理已更改其默认缓存策略。从 2.7 版(2008 年 5 月)和 3.1 版(2010 年 3 月)开始,默认行为是缓存动态内容。
            【解决方案8】:

            一般来说这应该没问题,但如果有一个中间缓存(代理)被配置为忽略请求参数,则它可能不起作用。

            例如,如果您通过 Akamai CDN 提供静态内容,则可以将其配置为忽略请求参数以防止使用此方法破坏缓存。

            【讨论】:

              【解决方案9】:

              将版本号放在实际文件名中更安全。这允许同时存在多个版本,因此您可以推出新版本,如果仍然存在请求旧版本的任何缓存 HTML 页面,他们将获得适用于其 HTML 的版本。

              注意,在 Internet 上最大的版本化部署之一中,jQuery 在实际文件名中使用版本号,它可以安全地允许多个版本共存,而无需任何特殊的服务器端逻辑(每个版本只是一个不同的文件)。

              当您部署新页面和新链接文件(这是您想要的)时,这会破坏缓存一次,然后可以有效地缓存这些版本(您也想要)。

              【讨论】:

              • 我同意这一点,但是让 Sinatra 将 ?v= 附加到所有 css 和 js 请求比必须单独控制每个文件要容易得多。最终我们将切换到 sinatra-assetpack,它将预处理和压缩所有文件,并实际在文件名后面附加一个版本号,这样我们就可以更轻松地单独控制它们。
              • 我同意如果您想确保 10000% 确定,将版本号放在文件名中是最安全的解决方案,但我不遵循“同时存在多个版本”的说法。具有查询参数的 URL 与具有不同查询参数的相同 URL 不同。客户应将它们视为两种不同的资源;如果不是,则客户端已损坏。
              • @Pekka - 版本号可以允许同时存在多个版本,但这需要服务器合作才能将查询参数映射到正确的实际文件。我认为这不是 OP 在这里所做的,并且没有理由要求在修改文件名时复杂得多,并且不需要服务器合作。显然两者都可以工作。
              【解决方案10】:

              它会破坏一次缓存,在客户端下载资源后,所有其他响应都将从客户端缓存中提供,除非:

              1. v 参数已更新。
              2. 客户端清除缓存

              【讨论】:

                【解决方案11】:

                参数?v=1.123 表示一个查询字符串,因此浏览器会认为它是一条来自?v=1.0 的新路径。从而导致它从文件加载,而不是从缓存加载。如你所愿。

                而且,浏览器会假定在您下次调用 ?v=1.123 时源将保持不变,并且 应该 使用该字符串缓存它。因此,无论您的服务器如何设置,它都会保持缓存状态,直到您移至 ?v=1.124 或以此类推。

                【讨论】:

                • 引用 Steve Souders 的话:“为了获得流行代理缓存的好处,请避免使用查询字符串进行修改,而是修改文件名本身。”完整的解释可以在这里找到:stevesouders.com/blog/2008/08/23/…
                • 那篇博文现在已经快十年了。你认为缓存提供者和 CDN 还没有适应它吗? Squid 似乎能够缓存带有查询字符串now 的文档。
                • 也许这对某人有帮助:就我个人而言,我使用文件修改时间戳作为“自动”版本参数,例如。 &lt;link rel="stylesheet" href="style.css?v=1487935578" /&gt;
                • 我个人不明白为什么,但 Lara Hogan (Swanson)(Etsy 的工程经理)不建议使用查询参数进行缓存清除。我认为这与用户和服务器之间的缓存代理有关。
                【解决方案12】:

                两个问题:这会有效地破坏缓存吗?

                是的。 Even Stack Overflow use this method, 虽然我记得他们(每天有数百万的访问者以及数以百万计的不同客户端和代理版本和配置)有一些奇怪的边缘情况,即使这还不足以破坏缓存。但一般的假设是这会起作用,并且是打破客户端缓存的合适方法。

                参数是否会导致浏览器从不缓存来自该 url 的响应,因为参数表明这是动态内容?

                没有。该参数不会改变缓存策略;服务器发送的缓存头仍然适用,如果它不发送任何,浏览器的默认值。

                【讨论】:

                • @spender 恐怕我现在找不到参考资料,有一篇很长的博客文章或 Jeff Atwood 谈到它的答案 (IIRC)
                • @spender 我读过一些代理服务器(旧的,或者可以配置为)缓存时忽略查询字符串。
                • @spender - 我也听说过,我认为更改文件名或路径是最好的选择。将所有静态文件移动到版本化文件夹名称下可能是最简单的,例如/static/v22/file.css,因为您可以使用单个文件夹重命名来执行多个文件,例如/static/v23/file.css/static/v23/mystuff.js
                猜你喜欢
                • 2018-05-27
                • 1970-01-01
                • 2015-07-12
                • 2018-06-01
                • 2020-09-07
                • 2011-08-21
                • 1970-01-01
                相关资源
                最近更新 更多