有没有人觉得 http 缓存设计的很不合理?

2022-01-24 10:25:17 +08:00
 3dwelcome

一般来说,WEB 是一个 html 主页面,里面包含若干个 css/js 。正常 html 是不缓存,确保浏览器每次访问都是最新的内容(服务器返回 200 或者 304)。

而 css/js 是时间策略缓存,也就是没过期之间(由 max-age 来定),浏览器压根不会向服务器发任何请求!就算 JS 代码有更新,短时间内用户也无法感知。

为了破除这个限制,webpack 都会在 js/css 后面加上 hash ,来解决这个问题。

那么问题来了,为什么 HTTP 不设计成,能一次性的向服务器请求 HTML 里所有 css/js 的最后更新时间( last-modified-time ),看看有哪些文件变动了,再针对性的获取呢? 这样 JS 就不用每次都改新文件名了。

4779 次点击
所在节点    前端开发
57 条回复
HeyWeGo
2022-01-24 11:33:26 +08:00
@kop1989smurf #10 幂等性?
3dwelcome
2022-01-24 12:04:59 +08:00
@serge001 "至于你说的方案,对于引入的第三方 js 怎么处理?"

引入第三方 JS ,就更需要我说的这个方案了。

举个例子,假设第三方 JS 被黑,很可能导致你主网站用户的 cookie ,全部被恶意泄漏。

能避免这样方法,就只有给 script 标签加上 hash ,做文件的内容二次校验。这和我说的标签加 ETAG ,是一回事。
lujjjh
2022-01-24 13:15:10 +08:00
0. 我理解你的方案可以用 querystring 里加 hash 做 cache busting ,同时加上 integrity 校验内容 hash 实现。
1. HTTP cache 不一定是 end-to-end 的,中间任意七层节点(比如 CDN 节点)都可以缓存,你还需要考虑你的这个机制怎么让中间节点知道某个资源缓存失效,上面的方案可能导致中间节点出现脏缓存。
2. 前端发布不是原子操作,发布过程中至少需要有两个版本共存,避免用户访问的时候下载到不同版本的资源。如果文件名不变,理论上是做不到无损发布的,这也是为什么现在基本没有人靠 querystring 来做 cache busting 了,基本都是在文件名里加 hash 。
id4alex
2022-01-24 13:26:03 +08:00
每次工程启动保存一个 unixtime, 然后资源文件 Link 加上?version=${unixtime}
wonderfulcxm
2022-01-24 13:37:48 +08:00
这跟 html 或 css/js 没关系吧,取决于服务器的响应头,如果你愿意,可以修改响应头,css/js 可以用 html 一样的缓存策略。
Jooooooooo
2022-01-24 13:46:30 +08:00
确实是历史原因.

可以再搜搜 http cache poison
3dwelcome
2022-01-24 13:46:44 +08:00
@wonderfulcxm "如果你愿意,可以修改响应头,css/js 可以用 html 一样的缓存策略。"

我本地测试用一样的策略啊,都是 max-age=0 ,本地流量无所谓,这样浏览器会自动获取新数据。

但是给客户用 max-age=0 ,页面有多少个 css 和 js ,就会多出多少条 HTTPS 链接,这设计显然不太合理。

所以我才说有缺陷。浏览器既然能识别 script 标签的 integrity 做内容校验,那顺便判断 ETAG ,节省网络资源的新 HTTPS 链接,就是举手之劳。
Pastsong
2022-01-24 13:47:46 +08:00
serviceWorker 解君愁
3dwelcome
2022-01-24 13:59:50 +08:00
@lujjjh “前端发布不是原子操作,发布过程中至少需要有两个版本共存”

这样一想,确实也有点道理。

如果一个新版 JS 要强制更新,万一有几十万个 Web 用户,同时下载 JS ,给服务器的压力也不少。

允许多个版本同时存在,渐进更新就没这个问题。
yaphets666
2022-01-24 14:57:07 +08:00
我明白了兄弟,你是要干翻现有前端体系
yuzo555
2022-01-24 15:12:07 +08:00
HTML 和 JS/CSS 并不一定是同一个提供者,而且一个网站引用的 JS/CSS 很有可能来自不同的提供者,比如同站和站外,比如不同的第三方 CDN ,甚至是同一个公司不同的团队(比如 A 产品团队需要使用到 B 产品,直接用 B 产品现成的 JS )。

统一提供一个接口获取更新时间比现行方案麻烦太多了,特别是如果某个 JS/CSS 中间代理、CDN 、负载均衡的套娃比较多,你根本不知道找谁确认更新时间。

如果是你说的这种,HTML 和 JS/CSS 属于同一个提供者、同一个服务器的情况,这个提供者自己在服务器端修改响应头就可以了,响应头控制得好,比你的方案能少一次请求。

也许你担心的是在缓存期内突然来了个 bug 需要紧急修复,这种属于特殊场景,加 hash 的方案就挺合适的,怎么也比你的方案要简单。

“就会多出多少条 HTTPS 链接”,HTTPS 连接都是复用的,不需要每次都重新来一遍,没你想的那么耗能。而且如你所说,如果文件没更新,返回 304 也非常省流。
0ZXYDDu796nVCFxq
2022-01-24 15:30:01 +08:00
cache-control + hash 是最符合 KISS 的
甚至,cache-control 都够了
ch2
2022-01-24 15:48:04 +08:00
你可以先发 head 请求,响应头里会告诉你上次变更的时间
ch2
2022-01-24 15:55:02 +08:00
@3dwelcome pwa 可以解决这个问题,前端自主拦截请求,使用本地缓存,这样用户刷新就不会多发 10 个请求了
otakustay
2022-01-24 19:05:32 +08:00
或者换个角度,用 resource integrity 来控制浏览器缓存( integrity 和 cache 里的一样就用,不一样就请求再检查 integrity )可能是更好的实现
ryd994
2022-01-25 02:19:58 +08:00
这不就是加 hash 吗?你绕了一圈还不是回来了。
服务器直接把你说的 last-modify 的 tag 加到 css 的 URI 里,你看你得到了什么?
比如 example.com/static/lastmodify_12345678/abc.css

Nginx 识别处理这种 URI 很容易
NewYear
2022-01-25 08:44:23 +08:00
楼主说的第二条,现在浏览器已经支持了,link 和 script 标签都支持 integrity 就是填入文件的 hash ,这样还能跨网站缓存(意思是 A 网站缓存过这个文件,B 网站就直接使用,而且 A 和 B 的 JS 文件的 URL 是截然不同的):
2. 用〈 script src='file.js' etag='hash'〉

就像我上面 2 楼回复的一样,你只是在“提建议”,却不给“解决办法”,你自己也不愿意动手写,自己写其实这玩意是很简单的一个事情啊!然后你又没有去找资料,明明浏览器已经支持了,你还在说没人推动,怎么就没人推动实现呢,明明是已经实现了的!!!就等着你去用的!

你可以看一下,我回复了 2 条内容,都是有明确的解决办法,你提了 N 个问题,有 N 个想法,却只是在“我想要”的层面。我的方法 1 ,如果是你的网站,那完全是在控制范围内,方法 2 ,是跨域使用 js 文件,你还想要继续讨论得到方法 3 吗。。。

好吧,那我提一个方法 3 ,随便一个能读写文件的语言,都能实现自动生成时间属性、时间后缀、hash 后缀并写入你的 html 文件,

请结贴,哦哦你想要偷懒,让 HTTP 服务器自动干这个事?其实也完全没问题啊,都是开源的,改一改一定能实现的。等等……你不会是想要“提一个意见,天下都听我的按我的方案来!”这样的吧。这样的话就尴尬了,毕竟谁也不缺一个想法啊。。
3dwelcome
2022-01-25 09:20:04 +08:00
@NewYear integrity 是内容强校验 hash ,仅仅只是确保内容是否正确。

integrity hash 和缓存里的 etag hash 是完全隔离的,又不能相互调用。

你说让我用,问题是这样写,没法用啊。我觉得你搜到的 cache ,应该是 cdn 的 cache,不是浏览器的 cache 。
3dwelcome
2022-01-25 09:29:33 +08:00
@ryd994 “服务器直接把你说的 last-modify 的 tag 加到 css 的 URI 里,你看你得到了什么?”

就是不希望加在 URI 里。问题的核心,我就希望只请求一个 HTML ,里面包含了足够的 js/css 校验信息给浏览器。

浏览器本地匹配完缓存资源后,如果没有新资源更新(属于常态),那后续没有任何的服务器 URL 请求!

我就想把这 css/js 校验流量,给节省下来,在我看来是完全多余的。HTTPS 返回 304 是很快,可是 DNS 解析,有时候会卡半天,属于客户端不可控因素。
NewYear
2022-01-25 10:03:02 +08:00
@3dwelcome

“integrity 是内容强校验 hash ,仅仅只是确保内容是否正确。”
如果浏览器不按 integrity 做缓存,那我觉得才叫奇葩,为什么呢,我认为这个属性重要的功能应该是 2 项,1.防止执行未知的 js 文件内容,带来了安全的效果; 2.跨网站做缓存(例如大家都调用了 vue2.0 ,只要第一个网站加载过,第二个网站就再也不用请求这个 js 文件了)。

但是我们之所以写 script 标签,写 src ,99%的目的是什么呢?最大的目的仍然是“我要执行这个文件里面的 js 语句”,而不是“我只是为了检验 src 的 js 文件是否符合预期”。



重点:我做了测试,Firefox 会按照 integrity 做缓存,新的 url 但是 integrity 相同,不会发出请求,Chrome 我就不测了。

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

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

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

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

© 2021 V2EX