javascript - 文件缓存 : Query string vs Last-Modified?

标签 javascript html css apache caching

我正在尝试缓存我网站 Assets 的方法,并注意到大多数与我类似的网站都使用查询字符串来覆盖缓存(例如:/css/style.css?v=124942823)

之后,我注意到每当我保存我的 style.css 文件时,最后修改的标题都会“更新”,使得查询字符串变得不必要。

所以我想知道:

  • 为什么这么多网站使用“查询字符串”方法,而不是让最后修改的 header 发挥作用?
  • 我应该取消设置 Last-modified header 并只处理查询字符串吗? (这有什么特别的好处吗?)
  • 最佳答案

    TL; 博士

    Why do so many websites use the "query string" method, instead of just letting the last-modified header do its work?



    更改查询字符串会更改 url,确保内容“新鲜”。

    Should I unset the Last-modified header and just work with query strings?



    不。虽然这几乎是正确的答案。

    网络上使用了三种基本的缓存策略:
  • 没有缓存,或缓存被禁用
  • 使用验证/条件请求
  • 永久缓存

  • 为了说明所有三个,请考虑以下场景:

    用户第一次访问网站,加载十个页面并离开。每个页面加载相同的 css 文件。对于上述每种缓存策略,会发出多少请求?

    无缓存:10 个请求

    在这种情况下,应该清楚没有其他任何影响结果的因素,对 css 文件的 10 次请求将导致它被发送到客户端(浏览器)10 次。

    好处
  • 内容永远新鲜
  • 无需努力/管理

  • 缺点
  • 效率最低,内容总是传输

  • 验证请求:10 个请求

    Last-ModifiedEtag使用,也会有 10 个请求。但是,其中 9 个将仅作为标题,而不会传输任何正文。客户端使用条件请求来避免重新下载已有的内容。以本网站的 css 文件为例。

    第一次请求文件时,会发生以下情况:
    $ curl -i http://cdn.sstatic.net/stackoverflow/all.css
    HTTP/1.1 200 OK
    Server: cloudflare-nginx
    Date: Mon, 12 May 2014 07:38:31 GMT
    Content-Type: text/css
    Connection: keep-alive
    Set-Cookie: __cfduid=d3fa9eddf76d614f83603a42f3e552f961399880311549; expires=Mon, 23-Dec-2019 23:50:00 GMT; path=/; domain=.sstatic.net; HttpOnly
    Cache-Control: public, max-age=604800
    Last-Modified: Wed, 30 Apr 2014 22:09:37 GMT
    ETag: "8026e7dfc064cf1:0"
    Vary: Accept-Encoding
    CF-Cache-Status: HIT
    Expires: Mon, 19 May 2014 07:38:31 GMT
    CF-RAY: 1294f50b2d6b08de-CDG
    .avatar-change:hover{backgro.....Some KB of content
    

    对同一 url 的后续请求将如下所示:
    $ curl -i -H "If-Modified-Since:Wed, 30 Apr 2014 22:09:37 GMT" http://cdn.sstatic.net/stackoverflow/all.css
    HTTP/1.1 304 Not Modified
    Server: cloudflare-nginx
    Date: Mon, 12 May 2014 07:40:11 GMT
    Content-Type: text/css
    Connection: keep-alive
    Set-Cookie: __cfduid=d0cc5afd385060dd8ba26265f0ebf40f81399880411024; expires=Mon, 23-Dec-2019 23:50:00 GMT; path=/; domain=.sstatic.net; HttpOnly
    Cache-Control: public, max-age=604800
    Last-Modified: Wed, 30 Apr 2014 22:09:37 GMT
    ETag: "8026e7dfc064cf1:0"
    Vary: Accept-Encoding
    CF-Cache-Status: HIT
    Expires: Mon, 19 May 2014 07:40:11 GMT
    CF-RAY: 1294f778e75d04a3-CDG
    

    注意没有正文,响应是 304 Not Modified .这告诉客户端它已经拥有(在本地缓存中)该 url 的内容仍然是新鲜的。

    这并不是说这是最佳方案。使用 the network tab of chrome developer tools 等工具允许您准确查看请求需要多长时间和做什么:

    enter image description here

    因为响应没有正文,所以响应时间会少得多,因为要传输的数据更少。但还是有回应。并且仍然存在连接到远程服务器的所有开销。

    好处
  • 内容永远新鲜
  • 仅发送了一个“完整”请求
  • 仅包含 header 的 9 个请求要精简得多
  • 更高效

  • 缺点
  • 仍然发出最大请求数
  • 仍然会导致 DNS 查找
  • 仍然需要建立到远程服务器的连接
  • 不能离线使用
  • 可能需要服务器配置

  • 永远缓存:1 个请求

    如果没有 etags,没有最后修改的 header ,并且只有一个在 future 很远设置的过期 header - 只有第一次访问 url 才会导致与远程服务器的任何通信。这是一个 well-known? best practice for better frontend performance .如果是这种情况,对于后续请求,客户端将从它自己的缓存中读取内容,而根本不与远程服务器通信。

    这具有明显的性能优势,这在延迟可能很大的移动设备上尤其显着(委婉地说)。

    好处
  • 最高效,内容只传输一次

  • 缺点
  • 网址 必须更改以防止现有访问者加载陈旧的缓存版本
  • 最努力的设置/管理

  • 不要使用查询字符串进行缓存破坏

    站点使用查询参数是为了绕过客户端的缓存。当内容更改时(或者如果发布了新版本的站点),查询参数会被修改,因此当 url 更改时,将请求该文件的新版本。这比每次更改文件时重命名文件更少工作/更方便,但它并非没有问题,

    Using query strings prevents proxy caching ,在下面的引用中,作者证明来自浏览器<->代理缓存服务器<->网站的请求不使用代理缓存:

    Loading mylogo.gif?v=1.2 twice (clearing the cache in between) results in these headers:

    >> GET http://stevesouders.com/mylogo.gif?v=1.2 HTTP/1.1
    << HTTP/1.0 200 OK
    << Date: Sat, 23 Aug 2008 00:19:34 GMT
    << Expires: Tue, 21 Aug 2018 00:19:34 GMT
    << X-Cache: MISS from someserver.com
    << X-Cache-Lookup: MISS from someserver.com
    
    >> GET http://stevesouders.com/mylogo.gif?v=1.2 HTTP/1.1
    << HTTP/1.0 200 OK
    << Date: Sat, 23 Aug 2008 00:19:47 GMT
    << Expires: Tue, 21 Aug 2018 00:19:47 GMT
    << X-Cache: MISS from someserver.com
    << X-Cache-Lookup: MISS from someserver.com
    

    Here it’s clear the second response was not served by the proxy: the caching response headers say MISS, the Date and Expires values change, and tailing the stevesouders.com access log shows two hits.



    这不应该掉以轻心 - 当访问物理上位于世界另一端的网站时,响应时间可能非常慢。从位于沿途的代理服务器获取答案可能意味着网站可用与否之间的区别 - 在永久缓存资源的情况下,这意味着 url 的第一次加载很慢,在使用验证请求的情况下意味着整个网站将变得缓慢。

    取而代之的是版本控制 Assets

    “最佳”解决方案是版本控制文件,以便每当内容更改时 url 也会更改。通常,这将作为构建过程的一部分自动化。

    然而,几乎要妥协的是实现重写规则 such as
    # ------------------------------------------------------------------------------
    # | Filename-based cache busting                                               |
    # ------------------------------------------------------------------------------
    
    # If you're not using a build process to manage your filename version revving,
    # you might want to consider enabling the following directives to route all
    # requests such as `/css/style.12345.css` to `/css/style.css`.
    
    # To understand why this is important and a better idea than `*.css?v231`, read:
    # http://stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring
    
    <IfModule mod_rewrite.c>
       RewriteCond %{REQUEST_FILENAME} !-f
       RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpe?g|gif)$ $1.$3 [L]
    </IfModule>
    

    这样一个请求 foo.123.css被服务器处理为 foo.css - 这具有使用查询参数进行缓存破坏的所有优点,但没有禁用代理缓存的问题。

    关于javascript - 文件缓存 : Query string vs Last-Modified?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23603023/

    相关文章:

    html - 删除 div 下面的异常填充

    html - Twitter Bootstrap 3 - 如何根据屏幕尺寸从水平按钮组切换到垂直按钮组?

    CSS - jQuery Layout & Bootstrap 3 - Z-Index 问题?

    javascript - 如何检查 Chrome 移动浏览器中是否授予/阻止了增强现实权限?

    java html5 websocket 服务器和客户端示例?

    html - 为什么第二个 flexbox 容器(里面有一个 SVG)向上移动?

    javascript - Bootstrap 3 导航栏没有完全折叠

    javascript - 在 div 中固定位置背景图像

    javascript - DOM 异常 : Blocked a frame with origin "http://localhost" from accessing a cross-origin frame

    javascript - 如何使用react-router v4获取mapStateToProps中的params值?