浏览器缓存主要分为强缓存(也称本地缓存)和协商缓存(也称弱缓存)。
强缓存
使用强缓存策略时,如果缓存资源有效,则直接使用缓存资源,不必再向服务器发起请求。
强缓存策略可以通过两种方式来设置,分别是 http
头信息中的 Expires
属性和 Cache-Control
属性。
Expires
属性,来指定资源的过期时间。在过期时间以内,该资源可以被缓存使用,不必再向服务器发送请求。这个时间是一个绝对时间,它是服务器的时间,因此可能存在这样的问题,就是客户端的时间和服务器端的时间不一致,或者用户可以对客户端时间进行修改的情况,这样就可能会影响缓存命中的结果。Expires
是 http1.0 中的方式,因为它的一些缺点,在 HTTP 1.1 中提出了一个新的头部属性就是 Cache-Control
属性,它提供了对资源的缓存的更精确的控制。它有很多不同的值,如下:Cache-Control 可设置的字段:
public: 设置了该字段值的资源表示可以被任何对象(包括:发送请求的客户端、代理服务器等等)缓存。这个字段值不常用,一般还是使用 max-age
来精确控制;
private: 设置了该字段值的资源只能被用户浏览器缓存,不允许任何代理服务器缓存。在实际开发当中,对于一些含有用户信息的 HTML,通常都要设置这个字段值,避免代理服务器(CDN)缓存;
no-cache: 设置了该字段需要先和服务端确认返回的资源是否发生了变化,如果资源未发生变化,则直接使用缓存好的资源;
no-store: 设置了该字段表示禁止任何缓存,每次都会向服务端发起新的请求,拉取最新的资源;
max-age: 设置缓存的最大有效期,单位为秒;
s-maxage: 优先级高于 max-age
,仅适用于共享缓存(CDN),优先级高于 max-age
或者 Expires
头;
max-stale: 设置了该字段表明客户端愿意接收已经过期的资源,但是不能超过给定的时间限制。
一般来说只需要设置其中一种方式就可以实现强缓存策略,当两种方式一起使用时,Cache-Control
的优先级要高于 Expires
。
no-cache 和 no-store 很容易混淆:
no-cache
是指先要和服务器确认是否有资源更新,在进行判断。也就是说没有强缓存,但是会有协商缓存;no-store
是指不使用任何缓存,每次请求都直接从服务器获取资源。协商缓存
如果命中强制缓存,我们无需发起新的请求,直接使用缓存内容,如果没有命中强制缓存,如果设置了协商缓存,这个时候协商缓存就会发挥作用了。
上面已经说到了,命中协商缓存的条件有两个:
使用协商缓存策略时,会先向服务器发送一个请求,如果资源没有发生修改,则返回一个 304 状态,让浏览器使用本地的缓存副本。如果资源发生了修改,则返回修改后的资源。
协商缓存也可以通过两种方式来设置,分别是 http
头信息中的 Etag
和 Last-Modified
属性。
Last-Modified
属性来指出资源最后一次修改的时间,当浏览器下一次发起请求时,会在请求头中添加一个 If-Modified-Since
的属性,属性值为上一次资源返回时的 Last-Modified
的值。当请求发送到服务器后服务器会通过这个属性来和资源的最后一次的修改时间来进行比较,以此来判断资源是否做了修改。如果资源没有修改,那么返回 304
状态,让客户端使用本地的缓存。如果资源已经被修改了,则返回修改后的资源。使用这种方法有一个缺点,就是 Last-Modified
标注的最后修改时间只能精确到秒级,如果某些文件在 1
秒钟以内,被修改多次的话,那么文件已将改变了但是 Last-Modified
却没有改变,这样会造成缓存命中的不准确。Last-Modified
的这种可能发生的不准确性,http
中提供了另外一种方式,那就是 Etag
属性。服务器在返回资源的时候,在头信息中添加了 Etag
属性,这个属性是资源生成的唯一标识符,当资源发生改变的时候,这个值也会发生改变。在下一次资源请求时,浏览器会在请求头中添加一个 If-None-Match
属性,这个属性的值就是上次返回的资源的 Etag
的值。服务接收到请求后会根据这个值来和资源当前的 Etag
的值来进行比较,以此来判断资源是否发生改变,是否需要返回资源。通过这种方式,比 Last-Modified
的方式更加精确。当 Last-Modified
和 Etag
属性同时出现的时候,Etag
的优先级更高。使用协商缓存的时候,服务器需要考虑负载平衡的问题,因此多个服务器上资源的 Last-Modified
应该保持一致,因为每个服务器上 Etag
的值都不一样,因此在考虑负载平衡时,最好不要设置 Etag
属性。
总结:
强缓存策略和协商缓存策略在缓存命中时都会直接使用本地的缓存副本,区别只在于协商缓存会向服务器发送一次请求。它们缓存不命中时,都会向服务器发送请求来获取资源。在实际的缓存机制中,强缓存策略和协商缓存策略是一起合作使用的。浏览器首先会根据请求的信息判断,强缓存是否命中,如果命中则直接使用资源。如果不命中则根据头信息向服务器发起请求,使用协商缓存,如果协商缓存命中的话,则服务器不返回资源,浏览器直接使用本地资源的副本,如果协商缓存不命中,则浏览器返回最新的资源给浏览器。
使用缓存有下面的优点:
用 webpack
打包文件,除了 index.html
入口文件不缓存,其他静态文件用 hash
值命名,hash
值不变就走强缓存,否则就走协商缓存。
cookie 和 session 的区别:
cookie
数据存放在客户端,session
数据放在服务器端。cookie
本身并不安全,考虑到安全应当使用 session
。session
会在一定时间内保存在服务器上。如果访问量比较大,会比较消耗服务器的性能。考虑到减轻服务器性能方面的开销,应当使用 cookie
。cookie
保存的数据不能超过 4K
,很多浏览器都限制一个域名最多保存 50
个 cookie
。将登陆信息等重要信息存放为 session
、其他信息如果需要保留,可以放在 cookie
中。
cookie、localStorage 以及 sessionStorage 的异同点:
分类 | 生命周期 | 存储容量 | 存储位置 |
---|---|---|---|
cookie | 默认保存在内存中,随浏览器关闭失效(如果设置过期时间,在到过期时间后失效) | 4KB | 保存在客户端,每次请求时都会带上 |
localStorage | 理论上永久有效的,除非主动清除。 | 4.98MB(不同浏览器情况不同,safari 2.49M) | 保存在客户端,不与服务端交互。节省网络流量 |
sessionStorage | 仅在当前网页会话下有效,关闭页面或浏览器后会被清除。 | 4.98MB(部分浏览器没有限制) | 保存在客户端,不与服务端交互。节省网络流量 |
应用场景:
request
对象中配置 “withCredentials”: true;response
的 header
中配置"Access-Control-Allow-Origin", “http://xxx:${port}”;response
的 header
中配置"Access-Control-Allow-Credentials", “true”为什么会有预检请求?
预检请求的发生,来源于浏览器的跨域请求,浏览器对跨域的处理方式一般有两种:
一般情况下,跨域产生是第二种情况,服务器对数据已经进行了处理然而结果被浏
览器拦截了,造成请求失败。
所以为了避免这种情况,浏览器使用了 HTTP
的 OPTIONS
方法发起了一个预检请求,预检请求成功之后才会发起真实的带数据的请求,否则阻止。
何种情况才会触发预检请求呢?
CORS 分为两种请求:简单请求和非简单请求。
简单请求
GET
、POST
、HEAD
请求application/x-www-form-urlencoded
、multipart/form-data
、text/plain
Accept
、Accept-Language
、Content-Language
、DPR
、Downlink
、Save-Data
、Viewport-Width
、Width
XMLHttpRequestUpload
对象没有注册任何事件监听器;XMLHttpRequestUpload
对象可以使用 XMLHttpRequest.upload 属性访问。ReadableStream
对象非简单请求
GET
、POST
、HEAD
请求以外的其他请求Content-Type
的类型:不属于简单请求的类型的以外的类型当是非简单请求时,浏览器会先进行一次预检请求以确定能否正常访问,是一种对数据修改的保护。