浏览器缓存

HTTP 是无状态的协议,对于事务处理没有记忆能力,每次请求完成后服务器不会保存任何会话信息,为了跟踪请求者的身份和会话状态,标示多个请求是不是来自于同一客户端,需要主动维护一个状态,因此通过 cookie 或 session 实现

在客户端 JS 中可以通过 document.cookie 访问和修改,Domain 和 Path 必须设置为对应 cookie 相同的值,否则会创建新的 cookie

cookie 可以手动设置过期时间,通常保持时间较长

cookie 中不允许存在逗号、分号、空格,可以使用 encodeURLComponent() 进行处理

浏览器一般总共最多存 300 个 cookie,而针对同一个网站最多存 20 个 cookie

单个 cookie 数据最大容量为 4KB 左右,根据不同浏览器可能会有偏差

Session 是一种记录服务器和客户端对话状态的机制,存储容量远高于 cookie。一般失效时间比较短,客户端关闭或超时会默认失效

cookie 和 session 都可以用来认证用户身份的信息,区别在于 cookie 保存在客户端(浏览器),而 session 保存在服务器端

因为 session 存储在服务器端,当同时在线用户量,也就是 session 过多时,会占据较多服务器内存资源,需要服务端定期清理过期 session

浏览器第一次访问服务器时服务器创建 session 信息,并将 session ID 放入 cookie 中返回给客户端,客户端在后续访问时携带此 cookie,服务器即可查找对应的 session 信息进行认证

Session 并不一定要靠 cookie 实现,当浏览器禁用 cookie 时,也可以通过重写 url 参数的方式发送 session ID

单点登录(SSO) / 前后端鉴权的常用实现方式

  1. Cookie + Session
  2. Token(JWT)

由于移动端对 cookie 的支持较差,通常使用 token 的方式实现移动端鉴权

WebStorage

  • localStorage

场景:在线编辑的自动保存、多页面访问共同数据、存储 base64 图片资源

没有存储时间限制,需要手动清除

一般容量在 5MB 左右

仅支持存储 string 类型数据,不支持 JSON,以键值对形式存储。本质上是对字符串的读取,如果内容过多消耗内存空间会使页面变卡

1
2
3
4
5
localStorage.setItem('key', 'val');
let value = localStorage.getItem('key');
localStorage.removeItem('key');
localStorage.clear();

  • sessionStorage

场景:SPA 单页应用组件间传值、浏览记录、填写表单信息

并不是持久化存储,浏览器关闭即清除

容量也在 5MB 左右

同样以键值对的形式保存数据,值为字符串

IndexedDB

运行在浏览器中的非关系型数据库,内部采用对象仓库存储数据,以键值对的方式存储

理论上没有容量上限

数据库的读写属于 I/O 操作,是异步的

同样受同源策略限制,无法访问跨域的库

离线缓存

支持没有网络连接状态下浏览页面

HTTP 缓存

浏览器缓存指的时将某一次请求的网络资源保存下来,再次请求相同内容时可以直接使用,从而大大提升响应速度,减少网络和服务器压力

浏览器缓存通过响应头中的 Cache-Control 字段控制,当响应头中有相关字段时,浏览器会保存资源到本地

响应头中,和浏览器缓存相关的缓存信息有:

  • Cache-Control 资源过期时长,no-cache 缓存但每次询问(协商缓存)/ no-store 不缓存
  • Date 资源返回的时间
  • ETag 资源的唯一标识符,作用类似于文件指纹
  • Last-Modified 资源上次修改时间

在下一次请求相同资源时,浏览器会优先查看缓存列表,如果有缓存,则会计算过期时间,检查是否有效

当缓存有效时,浏览器实际上不会发出请求,而是直接使用本地缓存,并返回 200 状态码,这种情况被称为强缓存

当缓存过期时,浏览器也不会直接发出请求,而是首先发出带缓存信息的请求(请求头中发送 if-Modified-Since 和 if-None-Match),询问服务器资源是否变化,这种情况被称为协商缓存

如果无变化,服务器返回 304 和新的缓存信息,同时无响应体,此时浏览器继续使用缓存中的资源

如果有变化,服务器返回 200 和新的缓存信息,响应体中携带新的资源,浏览器加载新资源并缓存

简单来说,强缓存完全不访问服务器,而协商缓存会有和服务器请求交互的过程

如果请求头中设置 Cache-Control: no-cache 表示让服务器正常处理请求不考虑缓存问题

通常来说,对于有哈希值文件名(即指纹)的文件,直接设置强缓存即可。而像 HTML 文件一般不带有哈希值,此时设置协商缓存

有时在项目中,静态资源很少变动,如静态官网,如果每次运行和更新都将静态资源一同打包会浪费资源和时间,因此可能分开管理代码与静态资源,静态资源不用构建工具进行打包,也因此静态资源不会有文件指纹哈希值,这种情况下如果设置了 HTTP 缓存,会导致更新静态资源后,线上环境的客户需要手动清理缓存才能看到最新的页面,这种情况最简单的方式是更改静态资源时,手动改动一下文件名,不要与修改前相同

HTTP 缓存内容保存在客户端的实际位置有四个,请求发出后会按照如下顺序依次检查:

  • Service Worker: 运行在浏览器后的独立线程,传输协议必须为 HTTPS
  • Memory Cache: 客户端的内存中,速度快读取高效,但持续性差,一般关闭页面即释放。一般是脚本、字体、图片
  • Disk Cache: 客户端的硬盘中,绝大部分缓存保存的位置,速度慢但容量大、时效性长。一般是css等
  • Push Cache: HTTP2 中的内容,仅在 session 中存在,会话结束即释放

Chrome 浏览器会根据本地内存的使用率来决定将缓存放在什么位置,如果内存使用率很高就放在磁盘里,如果内存占用不高则会暂时存放在内存中

用户操作对应的情况:

  • 输入 url 访问:强缓存、协商缓存均有效。查找 disk cache,没有则发送请求
  • 普通刷新: 强缓存无效,协商缓存有效。优先查找 memory cache,没有则查找 disk cache
  • 强制刷新: 强缓存、协商缓存均无效。