通过脚本标签与 GET 包含时,JS 文件缓存是不同的

JS file caching is different when included via script tag vs GET

在过去的几天里,我一直陷入缓存地狱,而当我开始了解它时,它仍然有点挣扎。

想要的结果
我想在网站上包含一个 JS 文件并将其缓存 and/or 仅当服务器上的文件已更改时才获取该文件的新副本。我不能在文件名中使用版本标签或散列。文件名需要保持一致。

我在哪里
据我阅读此内容后的理解,使用 Etags 是解决此问题的完美方法。他们将在文件更改时更新,如果标签不匹配,将发送文件的新版本。当我通过浏览器请求文件时这似乎有效

所以你可以看到,在第一次请求时我得到了 200,下载大小为 292 B,在第二次请求中,因为标签匹配,我得到了 304,下载大小为 137 B(我假设只是 header 大小).伟大的! 这正是我想要的。

我的请求 header 寻找 etag:

我的问题
现在这在我从浏览器请求文件时有效,但是在脚本标记中添加 js 文件不会以相同的方式发送请求 headers。因此,打开以下 html 文件

<!DOCTYPE html>
<html>
    <body>
        <script src="https://myTestSite.com/testCache.js"> 
        </script>
    </body>
</html>

我的 js 文件每次都被提取,因为 if-none-match 请求 header 不存在。使用 html 脚本标签时,如何复制在上述场景中观察到的行为?

TL;DR
在使用 html 脚本标签而不是简单的浏览器 GET 请求时,如何使用 Etags 仅请求修改后的 JS 文件?

也许您应该尝试使用 MLHttpRequest:

下载文件
<!DOCTYPE html>
  <html>
    <body>
    <script> 
    (() => {
      var xhr = new XMLHttpRequest();
      xhr.open('GET', "https://myTestSite.com/testCache.js");
      xhr.setRequestHeader('if-modified-since', '/*...*/')
      xhr.setRequestHeader('if-none-match', '/*...*/')
      //add more headers if you need
      xhr.onreadystatechange = function(){
          if(this.readyState == 4){
              if(this.status == 200){
                  //succeed
              }else{
                  //failed
              }
          }
      },
      xhr.send();
    })()
    </script>
  </body>
</html>

我让它工作了,但并不完全像我预期的那样。这就是我最终要做的。

在我想从中提供我的 js 文件的目录中,我添加了一个 .htaccess 文件

<FilesMatch "\.(js|css)$">
    Header set Cache-Control "no-cache, must-revalidate"
</FilesMatch>

我意识到,如果我在本地将我的 js 文件添加到缓存中,它总是会从本地缓存中提取,甚至永远不会向服务器发出请求。因此,即使更改了 Etag,我的客户端也永远不会知道,因为该文件已经在本地缓存了。

所以,我决定完全不缓存文件并重新验证。因此,虽然我确实有轻微的 http headers 开销,但我总是会获得最新的文件,而不需要每次都获取整个文件。

另一个选项(可能更简单)是在资源 URI 的末尾添加一个随机查询字符串:

<script src="https://myTestSite.com/testCache.js?version=<random_value>"></script>

这仍然意味着您正在从服务器获取相同的文件(服务器在返回资源时将忽略查询字符串),因此您不必在后端进行任何更改。

随机值可以是auuid,这样可以保证随机性。另外,关键可能不一定 version.