且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

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

更新时间:2022-05-09 00:06:10

对于大约 2008 年的网站来说,现有的 30 个左右的答案是很好的建议.但是,对于现代的单页应用程序 (SPA),可能是时候重新考虑一些基本假设了……特别是 Web 服务器***只提供文件的最新版本.

The 30 or so existing answers are great advice for a circa 2008 website. However, when it comes to a modern, single-page application (SPA), it might be time to rethink some fundamental assumptions… specifically the idea that it is desirable for the web server to serve only the single, most recent version of a file.

假设您是一位用户,在您的浏览器中加载了 M 版 SPA:

Imagine you're a user that has version M of a SPA loaded into your browser:

  1. 您的CD 管道部署了新版本的N应用到服务器
  2. 您在 SPA 中导航,它会向服务器发送 XMLHttpRequest (XHR) 以获取 /some.template
  1. Your CD pipeline deploys the new version N of the application onto the server
  2. You navigate within the SPA, which sends an XMLHttpRequest (XHR) to the server to get /some.template

  • (您的浏览器尚未刷新页面,因此您仍在运行 M 版本)
  1. 服务器以/some.template 的内容作为响应——您希望它返回模板的MN 版本吗?
  1. The server responds with the contents of /some.template — do you want it to return version M or N of the template?

如果 /some.template 的格式在 MN 版本之间发生变化(或者文件被重命名或其他)您可能不希望将模板的 N 版发送到运行旧版M 解析器的浏览器.†

If the format of /some.template changed between versions M and N (or the file was renamed or whatever) you probably don't want version N of the template sent to the browser that's running the old version M of the parser.†

Web 应用程序在满足两个条件时会遇到此问题:

Web applications run into this issue when two conditions are met:

  • 在初始页面加载一段时间后异步请求资源
  • 应用程序逻辑假设有关资源内容的事物(在未来版本中可能会发生变化)

一旦您的应用程序需要并行提供多个版本,解决缓存和重新加载"变得微不足道:

Once your application needs to serve up multiple versions in parallel, solving caching and "reloading" becomes trivial:

  1. 将所有站点文件安装到版本化目录中:/v/...files.../v/...files...
  2. 设置 HTTP 标头以让浏览器永久缓存文件

  • (或者更好的是,将所有内容都放在 CDN 中)
  1. 更新所有 标签等以指向版本化目录之一中的该文件
  1. Update all <script> and <link> tags, etc. to point to that file in one of the versioned directories

最后一步听起来很棘手,因为它可能需要为服务器端或客户端代码中的每个 URL 调用 URL 构建器.或者您可以巧妙地使用 <base> 标记 并在一处更改当前版本.

That last step sounds tricky, as it could require calling a URL builder for every URL in your server-side or client-side code. Or you could just make clever use of the <base> tag and change the current version in one place.

† 解决此问题的一种方法是在新版本发布时强制浏览器重新加载所有内容.但是为了让任何正在进行的操作完成,最简单的方法可能仍然是并行支持至少两个版本:v-current 和 v-previous.

† One way around this is to be aggressive about forcing the browser to reload everything when a new version is released. But for the sake of letting any in-progress operations to complete, it may still be easiest to support at least two versions in parallel: v-current and v-previous.