V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
lete
V2EX  ›  Node.js

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

  •  2
     
  •   lete · 222 天前 · 5493 次点击
    这是一个创建于 222 天前的主题,其中的信息可能已经有所发展或是发生改变。

    很多时候,我们的网站上都会对静态资源开启 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 语法树编译回源代码即可

    第 1 条附言  ·  222 天前

    更详细的说明请看这篇文章: 《前端优化之静态资源缓存控制

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

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

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

    第 3 种: last-modifed-time ,它也是协商缓存,但区别在于 etag 判断的是标识(hash),last-modifed-time 判断的是最后修改时间,它同样需要把时间发送给服务器去判断
    yushiro
        20
    yushiro  
       222 天前 via iPhone
    这个方案能解决 90%以上的情况,但我曾经遇到过,缓存机制不看参数,只看?之前的 url ,所以后来还是用文件名 hash 的方案。
    我遇到的情况并非在普通浏览器中,好像是微信的 webview 还是哪个手机 app 环境里面。
    3dwelcome
        21
    3dwelcome  
       222 天前
    @lete 我原贴的意思是,如果浏览器直接读 html 里的 etag 或者 last-modifed-time ,就可以不协商,仅仅本地对比。

    现在浏览器没读,所以就只能发请求服务器进行协商。

    @yushiro 早期的手机浏览器设计很奇葩,会自作主张在 url 请求后面加?作为一些特殊标识。然后服务器收到两个?的参数,就会出问题了。
    lete
        22
    lete  
    OP
       222 天前
    @3dwelcome 哦哦,是我没看明白,哈哈哈

    不过 w3c 没制定标准,浏览器厂商是不可能去做的,况且如果有浏览器厂商做了,其它厂商也不一定做
    lete
        23
    lete  
    OP
       222 天前
    @yushiro 确实有些浏览器或套壳浏览器有这种问题
    liaohongxing
        24
    liaohongxing  
       222 天前
    vue-cli vite-cli nuxt-cli cra-cli nextjs-cli build 出来文件都自带 hash , 一般都是 index.html 设置 no-store 。index.html 里面引用的 js css images 跟着自动变 。感觉现在都用这种脚手架了吧
    CodingNaux
        25
    CodingNaux  
       222 天前
    这种常见问题,应该都有方案
    iqoo
        26
    iqoo  
       222 天前
    这方案十几年前就普遍使用了
    learningman
        27
    learningman  
       222 天前
    有的 CDN ignore query string 的,你这白操作
    lete
        28
    lete  
    OP
       222 天前
    @learningman 你可能理解错了,query string 主要是给浏览器辨别这个连接有没有缓存的,如果 url 变了就会像服务器索要新的资源,没变就直接充浏览器缓存中读取
    learningman
        29
    learningman  
       222 天前
    @lete #28 你改 query string 刷了浏览器的缓存,但是 CDN 的缓存还是可能没刷啊。。。不如乖乖配 Cache Control
    lete
        30
    lete  
    OP
       222 天前
    @learningman 不设置 CDN 的缓存不就可以了,直接 Cache-Control 设置强制缓存
    humbass
        31
    humbass  
       213 天前
    我的天啊,真是这么玩的吗 ?如果是 HTML 的变化,一般前段打包器就已经做了这个工作,比如 webpack ,只要打包内部的索引( hash )就自动换了。

    难道我理解错了 楼主 的意思吗
    whatFoxSay
        32
    whatFoxSay  
       118 天前
    这不就是协商缓存吗。。。
    lete
        33
    lete  
    OP
       114 天前
    @whatFoxSay 不是协商缓存呀
    关于   ·   帮助文档   ·   博客   ·   nftychat   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5606 人在线   最高记录 5634   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 50ms · UTC 02:06 · PVG 10:06 · LAX 19:06 · JFK 22:06
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.