浏览器缓存

    • 缓存规则:主要定义在 HTTP协议头
    • 缓存类型:本地缓存(标识过期机制 Expiration)和 协商缓存(标识验证机制 Validation),当本地缓存失效时启用协商缓存

    参考阅读:

    本地缓存

    当本地缓存命中时不会发送请求。缓存规则是通过协议头字段 Cache-Control 定义(以及旧版本规范中的 Expires / Pragma);优先级 Cache-Control > Expires.

    注:也可以在 HTML 头中的 Meta 标签中 http-equiv 属性中定义(关联到 HTTP头部),如 <meta http-equiv="Cache-Control" content="no-cache">

    Cache-Control

    Cache-Control: public, max-age=600
    

    字段的参数是可选的,多个指令间通过 , 分割

    • 表示能否缓存
      • public: 可以被所有用户缓存,包括中间代理(如 CDN)
      • private: 仅被终端用户缓存
      • no-cache: 不使用本地缓存(不妨碍使用协商缓存),End-to-End Revalidate,即端对端重载
    • 表示缓存期限
      • max-age: 缓存最长时间(秒)

    关于 max-age=0no-cache 的区别:http://stackoverflow.com/questions/1046966/whats-the-difference-between-cache-control-max-age-0-and-no-cache

    注:HTTP 1.0 中 Expires 返回的是缓存失效的绝对时间,如果服务端和客户端时间不同步,会造成缓存正确性问题

    RFC 2616, Cache-Control is preferred over Expires: This might be useful if certain HTTP/1.0 caches improperly calculate ages or expiration times, perhaps due to desynchronized clocks.

    协商缓存

    客户端发送本地缓存的信息以请求服务端校验,如果校验成功返回 304 Not Modified,否则返回完整资源内容、并更新 Last-Modified / ETag.

    校验标准为 资源内容(ETag,通常是资源文件的摘要信息,如 MD5 Hash)或 资源最后修改时间(Last-Modified),优先级 ETag > Last-Modified,当 ETag 一致时、继续对比 Last-Modified.

    注:Last-Modified 因为只能精确到秒,所以是一种 Weak Validator.

    请求报文中会相应携带 If-None-Match / If-Modified-Since 条件请求。

    例如,首次请求的响应头示例:

    Cache-Control: max-age=604800
    ETag: "544b331b-189d"
    Expires: Sat, 16 Apr 2016 07:40:27 GMT
    Last-Modified: Sat, 25 Oct 2014 05:20:27 GMT
    

    后续请求的请求头:

    If-Modified-Since: Sat, 25 Oct 2014 05:20:27 GMT
    If-None-Match: "544b331b-189d"
    

    用户操作与缓存

    用户在网页上的不同操作,会影响浏览器使用缓存的策略

    操作 Cache-Control / Expires Last-Modified/ ETag
    地址栏回车 有效 有效
    页面链接跳转 有效 有效
    新建窗口 有效 有效
    前进/后退 有效 有效
    刷新(F5) 无效 有效
    强制刷新(Ctrl+F5) 无效 无效

    用户主动刷新时,会忽略本地缓存( Reload == Revalidation ),因此建议本地缓存和协商缓存配合使用。

    资源部署 / 缓存更新

    主体策略:

    • 资源过期前,尽可能使用本地缓存
    • 通过将文件内容指纹嵌入网址,强制客户端更新
    • 为了获得最佳性能,每个应用需要定义自己的缓存层级(减少 CDN 上的回源流量)

    摘自: https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=zh-cn

    入口页面 HTML 不使用缓存,这样可以修改其中资源文件名引用路径、以强制使用新的资源

    /page.html  [Cache-Control: no-cache]
        |-- /script.6adz234.js      [Cache-Control: max-age=31536000]
        |-- /main.css?v=2016.1.1    [Cache-Control: max-age=31536000]
    

    关于海量 Web 服务的前端代码部署问题,可以详细阅读:大公司里怎样开发和部署前端代码?

    注1:对于静态资源使用 Cache-Control: no-cache 会导致所有缓存代理服务器回源,是代价较高的行为。

    注2:如果无法修改资源引用(比如一些第三方脚本),策略是减少缓存时间、或通过一个不缓存的脚本加载器来指定主脚本的最新请求路径。

    百度统计脚本通过 must-revalidate 强制使用协商缓存

    Cache-Control: max-age=0, must-revalidate
    Etag: fae81a78f0f25842e9c8683673ab09c7
    

    Google Analysis 脚本还是启用了本地缓存的

    cache-control: public, max-age=7200
    expires: Sat, 09 Apr 2016 10:24:23 GMT
    last-modified: Mon, 28 Mar 2016 20:26:56 GMT
    

    注3:部分 ISP 的缓存代理服务器会不遵循 HTTP 1.1 协议,比如无视 Cache-Control 指令,强制缓存所有资源甚至动态 CGI. 参考阅读:http://www.cnblogs.com/_franky/archive/2012/07/05/2577141.html