如何保证 HTTP 强制缓存的新鲜度

2022-10-26 10:36:50 +08:00
 lete

很多时候,我们的网站上都会对静态资源开启 HTTP 缓存,HTTP 缓存分为两种,但对于静态资源来说,我想,大部分人开的因该是强制缓存吧?

不过强制缓存有个缺点那就是没法保证资源的 新鲜度(最新的) ,只能等待缓存时间过期才能获取到最新的资源内容

于是我写了一个库 Cache-Hash 在线求 star✨ ,专门处理 HTTP 缓存破除的

我的博客已经用上了: https://blog.imlete.cn

原理

通过给网站引入的资源,打上 hash 版本号,一旦内容改变,hash 会随着改变,这样即可通过改变 url 地址来破除缓存,能够保证网站所引用的资源是最新的

例如以下形式

<script src="https://demo.com/js/main.js?v=5e74b42bf5"></script>

使用方式

可以通过使用 CLI (命令行工具) ,也可以使用 JavaScript API 来对静态资源的引用生成 hash

可以全局安装使用

npm install cache-hash -g

cache-hash --target source --output public

# 简写

cache-hash -t source -o public

如果你不想全局安装,可以使用 npx

npx cache-hash --target source --output public

它是如何工作的?

它通过读取你给定的 target 目录,检测目录内的所有 html 、css 、js ,并对这些文件生成 AST(Abstract Syntax Tree) 即抽象语法树 ,之后通过修改 ast 语法树的内容后,再通过 ast 语法树编译回源代码即可

6159 次点击
所在节点    Node.js
33 条回复
1018ji
2022-10-26 10:39:45 +08:00
真是个好想法 大赞
czgaotian
2022-10-26 10:45:48 +08:00
前端打包出来的产物不是可以自带 hash 的吗
这个包的应用场景是什么样的呢
shyling
2022-10-26 10:49:28 +08:00
一般不都是 index.html 一直刷新,引用的 js,css 跟着版本变 hash 吗?
lete
2022-10-26 10:56:15 +08:00
@czgaotian 主要应用于再没有打包工具的情况下,比如一些项目的文档(当然有些文档生成框架是支持生成 hash 的),还有比如 hexo 、hugo 等这些打包出来的产物它们并没有生成 hash 的功能
lete
2022-10-26 10:58:01 +08:00
@shyling 不太明白你的意思,一直刷新是没用的,除非你清理浏览器缓存或者强制刷新网页(ctrl+f5)
shyling
2022-10-26 10:58:52 +08:00
@shyling #3 我的意思是 index.html 不缓存
killva4624
2022-10-26 11:22:37 +08:00
一般都直接用 cache-control 头来控制 index.html 就好了吧?
lete
2022-10-26 11:27:14 +08:00
@shyling 可是你的其它资源比如 css 、js 、img 、mp3 、font 、等用了缓存,你的 index.html 用的依然是缓存啊
lete
2022-10-26 11:31:17 +08:00
@killva4624 没错
shyling
2022-10-26 14:41:11 +08:00
@lete #8 是啊。。js 这些的路径自带 hash 呗
qW7bo2FbzbC0
2022-10-26 14:44:08 +08:00
cdn 厂商不是帮忙做这些吗
weizhen199
2022-10-26 14:46:41 +08:00
我想起以前在 IIS 上写 silverlight 的时候,IE 的缓存策略非常的顽固,所以 HTTP HEAD 的那些 cache ,hint 都不识,我们发布的时候就给 app 名字后面加版本号,在一些配置文件后面.config?ramdon=xx
lete
2022-10-26 14:52:26 +08:00
@shyling #10 怎么自带?手写?
3dwelcome
2022-10-26 14:56:26 +08:00
关于这问题,我以前发过贴 https://v2ex.com/t/830203

看来历史的轮子总是重复的。
whistle24
2022-10-26 14:57:47 +08:00
@shyling 正解,现在用 webpack 等打包的基本都有配置打包后文件带 hash 值
lete
2022-10-26 15:08:55 +08:00
@qW7bo2FbzbC0 cdn 有两种缓存
1. 缓存原服务器的静态资源,规则由你选择,在缓存期间,任何请求都只会从 cdn 的网络中响应资源给用户(你服务器的任何资源修改 cdn 都不会去刷新(除非你手动在 cdn 里刷新),只有当缓存时间过了之后才会向你服务器获取),在此期间不会对你的服务器有任何连接
2. 要么就是协商缓存,那么用户访问还是会去问服务器这个资源是不是最新的,要么就是强制缓存,这就是正常的强制缓存,无论服务器端怎么改变资源,浏览器都不会去访问服务器,只有过期了才会访问服务器
lete
2022-10-26 15:12:58 +08:00
@whistle24 本文的 cache-hash 工具是给没有自带生成 hash 功能的场景使用,比如一些文档生成工具,它们只负责将 markdown 渲染出一个个文档页面,并没有生成 hash 的功能,当然有些文档生成工具也有自带的比如 vuepress
whistle24
2022-10-26 15:21:40 +08:00
@lete 这样说的话,是可以理解的
lete
2022-10-26 15:30:43 +08:00
@3dwelcome 看了一下,你列举的 3 个方法,第 1 个没看明白,但后两个方法存在问题,虽然都能解决你帖子的疑问

第 2 种: 使用 etag 实际上是协商缓存,每次请求都会向服务器确认资源有没有变化,如果服务器线路比较拉跨,那么这个请求到服务器的时间也会随之变长,浏览器再等待服务器响应回来也需要时间,如果是强制缓存,就没有那么多的步骤,直接从浏览器本地缓存读取

第 3 种: last-modifed-time ,它也是协商缓存,但区别在于 etag 判断的是标识(hash),last-modifed-time 判断的是最后修改时间,它同样需要把时间发送给服务器去判断
yushiro
2022-10-26 16:25:21 +08:00
这个方案能解决 90%以上的情况,但我曾经遇到过,缓存机制不看参数,只看?之前的 url ,所以后来还是用文件名 hash 的方案。
我遇到的情况并非在普通浏览器中,好像是微信的 webview 还是哪个手机 app 环境里面。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/889921

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX