您绝对应该在 CSS 和 JavaScript(以及图像、字体和其他任何内容)等资产上 设置远期缓存标头。这告诉浏览器“基本上永远保留此文件”。这样,在网站上从一个页面导航到另一个页面时——或者重新访问它,或者刷新页面——浏览器就不需要再次下载它,从而产生更快的页面加载速度。这对于网页性能至关重要,所以一定要这样做!
但是,如何强制浏览器获取文件的最新版本呢?好吧,有很多方法。 查看该博文以了解更多信息。但这里有一个我最近使用过并希望记录下来的方法。
技巧是更改查询字符串
曾经有一段时间,普遍认为更改查询字符串是不够的,但即使在当时,它也不会起作用的原因也几乎都是极端情况。如今,更改查询字符串是可以的(假设您不更改默认行为,像 Cloudflare 这样的服务允许您这样做)。
因此,有一天您在 HTML 中这样发布它
<link rel="stylesheet" href="style.css?v=1" />
然后您更改该查询字符串以在需要时刷新缓存
<link rel="stylesheet" href="style.css?v=2" />
顺便说一下,HTML 要么未被缓存,要么缓存时间要短得多,因此将看到对 HTML 的更改。
我有时手动操作
多年来,我一直通过设置 PHP 变量并使用它来刷新此网站上的缓存,例如…
<?php $ver = 1.0; ?>
<link rel="stylesheet" href="style.css?v=<?php echo $ver; ?>" />
<link rel="stylesheet" href="some-other-asset.css?v=<?php echo $ver; ?>" />
嘿,这有效,但那是我手动操作该变量。我有时会忘记这样做,即使我记住了,我多少也有些讨厌不得不这样做。
使用 Gulp 自动化版本刷新
我目前在此站点上使用 Gulp 支持的构建流程,它执行所有经典操作:Sass、Babel、文件连接、实时重新加载…
我突然想到,我也可以让 Gulp 在每次更改 CSS 或 JavaScript 时更改查询字符串。JavaScript 具有 .replace()
方法,并且可以通过 gulp-replace 插件轻松地在 Node/Gulp 中使用。
我创建了一个任务。当我调用它时,它会在我的标题文件中查找字符串 cache_bust=
加上某个值,并用基于日期和时间的新的随机字符串替换它。
gulp.task("cache-bust-css", function() {
var cbString = new Date().getTime();
return gulp
.src(["header.php"])
.pipe(
replace(/cache_bust=\d+/g, function() {
return "cache_bust=" + cbString;
})
)
.pipe(gulp.dest("."));
});
当编辑和编译 JavaScript 文件时,我在一个单独的任务中执行相同操作。
它仍然有点笨拙
请注意,我每次更改任何 CSS 资产时都会更改所有 CSS 资产的查询字符串。这不像它那样高效。我可能应该只在已更改的文件上更改查询字符串。
我最终会解决这个问题。有时您只需要逐步改进解决方案。
这只是其中一种方法!还有其他专门用于此的 Gulp 插件。其他构建系统有不同的方法。这种方法碰巧非常适合我当时的确切需求。欢迎分享您的策略!
不要使用版本或日期时间,而是使用文件的哈希值来刷新缓存。这样,只有在文件更改时它才会更改。
或者,您可以使用实际的缓存标头或 ETag 标头,这正是它们的用途。在 URL 中执行此操作会将 HTML 和 CSS 绑定在一起,因此您现在需要对两者进行更改才能确保用户获得正确的版本。某些 CDN 不会将查询字符串视为唯一的 URL。
更改查询字符串不是处理缓存的方法。
我实际上已经做了十年了。
长期以来以某种特定方式做事并不意味着它是正确的。
查看 ETag 及其功能:https://mdn.org.cn/en-US/docs/Web/HTTP/Headers/ETag
可能值得记住的是,这从文件的角度起作用,而不是从请求它的任何内容的角度起作用。不要忘记,并非所有请求您的资源的内容都将在您的控制之下,因此 ETag 是一种为所有场景提供正确版本并正确执行缓存失效的方法。
@Ashley Sheridan 请阅读 https://css-tricks.org.cn/strategies-for-cache-busting-css/#article-header-id-4 和 https://stackoverflow.com/a/47535993/616067 了解有关 ETag 及其带来的问题的信息。
Ashley,
在我看来,使用 ETag 是缓存策略的一部分,但请记住,为了识别过时的 ETag,您仍然需要访问服务器,只是为了获得 304 – 未修改。如果您能保证资源在构建期间不过时,我认为最好在构建时执行此操作。在构建期间使用文件哈希和引用(例如“app-1ed9ab.js”)是一个非常好的方法,或者当您没有更高级的构建管道可用时,甚至可以使用查询字符串。这确保您可以使用远期过期缓存,其中浏览器甚至不需要与服务器进行检查。
嘿,Chris,
研究一下优秀的 gulp-rev 和 gulp-rev-replace 包来为您完成此操作。我们多年来一直在我的组织中使用它们,它们对于具有缓存刷新的 FFE 设置非常可靠。
干杯!
Simon
上次修改时间(日期)……这就是我要说的。;-)
老实说,希望移动版 Chrome 在您尝试显式刷新(如桌面浏览器)时能够实际刷新 CSS,但它似乎将其视为重新加载?(并且除非您诉诸于此类方法,否则它会愉快地保留旧样式表超过一周)强制刷新的唯一方法似乎是从设置中完全删除整个缓存?:/
当您提供静态 HTML 文件时(因为没有其他操作可以执行,并且您希望网站速度很快),这很烦人,因为这意味着必须更新所有内容才能进行较小的样式表更改。或者,只进行不会在未应用时造成破坏性影响的更改。
在我的 gulp 和 weppack 设置中 https://github.com/jmartsch/acegulp4 在文件 https://github.com/jmartsch/acegulp4/blob/master/gulpfile.babel.js/tasks/rev.js 中,我使用 gulp-rev-all 和 rev-manifest 根据文件内容生成哈希文件名。然后,我使用 PHP 函数获取要加载的图像、CSS 或 JS 文件的实际版本。
这很酷,我实际上使用 PHP 中的
filemtime
并使用该时间戳来执行此操作,只有修改的资产才会更改其查询字符串! :)我相信肯定有更优雅的方法来实现,但在只有一个主样式表的简单网站上,这种方法在 WordPress 中有效。
我看到一些评论提到了大家使用的时间戳方法。虽然这在某些情况下有效,但对于阅读本文的其他读者来说,请记住这可能并不适用于所有情况。例如,我们将我们的资产上传到 Microsoft Azure Blob Storage,它在资产创建时往往不会遵守或修改时间戳。你可能会遇到这种情况:一个未更改的资产在缓存中失效,随后被重新获取。在这种情况下,对文件内容进行哈希处理,虽然可能工作量稍微大一些,但更有优势。
你好!
我真的很喜欢这样做
<link rel="stylesheet" href="style.css?v=” />
@Eduard,你可以这样做,但这只是将问题转移到了另一个位置。你必须记住手动更新 v 参数,正如 Chris 所写:“我有时会忘记这样做,即使我记住了,我多少也有些不情愿去做。”
你也可以像其他人写的那样使用文件修改日期,这可能会导致文件内容未更改且文件修改日期被更改时重新加载。
在我看来,通过哈希文件名和分配映射(即 rev-manifest)进行缓存清除是最佳方案。