前端缓存
- 一、前言
- 二、web缓存分类
- 1. HTTP缓存:
- 2. 浏览器缓存:
- 3. Service Worker:
- 4. Web Storage缓存:
- 5. 内存缓存:
- 三、http缓存详解
- 1、http缓存类型
- a. 基于有效时间的缓存控制:
- b. 基于资源标识的缓存:
- c. 基于缓存存储位置的分类:
- d. 基于缓存位置的请求方式:
- 2. HTTP 缓存原理
- 3. HTTP 缓存策略
- 4. HTTP 缓存的优点和缺点
- a、优点:
- b、缺点:
- 四、强制缓存 和 协商缓存详解
- 1、强制缓存
- a、优点:
- b、缺点:
- c、使用场景:
- d、使用原理:
- e、基于Expires字段实现的强缓存
- f 、基于 Cache-Control 字段实现的强缓存(代替Expires的强缓存实现方法)
- 2. 协商缓存
- a、优点:
- b、缺点:
- c、使用场景:
- d、使用原理:
- e、基于Last-Modified / If-Modified-Since 的协商缓存
- f、基于ETag的协商缓存
一、前言
前端缓存是Web开发中一个非常重要的领域,它可以有效地提高页面的加载速度和用户体验,减轻后端服务器的负载。在面试中,中高级前端工程师往往需要展示对前端缓存的深入了解和实践经验。下面我来详细介绍一下前端缓存的相关知识点。
二、web缓存分类
1. HTTP缓存:
HTTP缓存是Web开发中最重要的缓存机制之一。HTTP协议支持多种缓存策略,其中
缓存控制
是控制浏览器缓存的最基本、最重要的机制。通过在HTTP响应头中设置Cache-Control
和Expires
等相关指令,可以让浏览器有效地缓存资源,提高响应速度。另外,ETag
和Last-Modified
是两种验证缓存是否过期的机制。
2. 浏览器缓存:
浏览器缓存是指浏览器可以对访问页面中的资源进行缓存,使得用户在二次访问页面时可以从本地缓存中读取资源,从而提高页面的加载速度。但是,浏览器缓存也可能会导致
资源更新不及时
的问题,需要在设置缓存时间进行平衡考虑。
3. Service Worker:
Service Worker是一种
Web Worker
,能够拦截网络请求,并根据定义的策略和缓存逻辑,返回缓存中的资源或者向服务器发起未缓存的网络请求。通过使用Service Worker
来实现离线缓存
,并将资源放到本地,可以有效地提高应用的访问速度和稳定性。
4. Web Storage缓存:
Web Storage是HTML5提供的浏览器本地存储API,包含了
localStorage
和sessionStorage
两种方式。它们能够快速地存储和检索本地数据,并且能够扩展到5MB或更大。Web Storage主要适用于在本地保存一些用户设置、登录状态等数据
,并与Service Worker
实现离线缓存
。
不要担心,这些概念对于未来会称为前端大牛的你来说都不是什么问题,非常的简单。因为太简单,数据缓存不再这篇文章的介绍中,这里一笔带过,需要了解的小伙伴,可以移步我的另一篇文章前端新能优化篇之localStorage和sessionStorage的区别及其使用方式
5. 内存缓存:
内存缓存是指将某些数据暂时放入内存中,以便在需要的时候能够快速地读取。内存缓存的优点是速度快,但缺点是
容易失去数据
。在进行性能优化
时,内存缓存通常会被使用。
总之,前端缓存是Web开发中一个非常庞大和重要的领域,中高级前端工程师需要了解各种缓存机制和策略,动态地制定缓存策略和缓存失效的管理机制,以提高页面的性能和用户体验。
HTTP 缓存是 Web 性能优化的关键。它可以通过降低资源请求次数 和 缩短资源请求时间 来显著提高网站的响应速度和性能。本文将详细介绍 HTTP 缓存的相关知识和使用。
三、http缓存详解
1、http缓存类型
HTTP 缓存可以分为多个方面,包括以下几种:
a. 基于有效时间的缓存控制:
使用
Cache-Control
和Expires
等字段来控制缓存时间,可将缓存分为强制缓存
和协商缓存
。
b. 基于资源标识的缓存:
使用
ETag
和Last-Modified
等字段通过比对实体的标识信息来判断缓存是否过期,同时也可以实现更细粒度的缓存控制。
c. 基于缓存存储位置的分类:
客户端缓存
:资源会被存储在客户端浏览器中,如浏览器缓存、HTTP 存储等。
服务器端缓存
:资源会被存储在服务器端,如 HTTP 服务器缓存、CDN 缓存等。
d. 基于缓存位置的请求方式:
根据条件判断(
ETag
和Last-Modified
等)是否需要重新向源服务器请求资源。
- 普通请求:是直接向原始服务器发送请求获取资源,
- 条件请求:是先向缓存服务器发送请求,
在实际应用过程中,可以根据不同的使用场景选择不同的缓存方案,以达到更好的性能优化和用户体验。
2. HTTP 缓存原理
HTTP 缓存的原理是基于客户端浏览器和 Web 服务器之间的通信过程。具体流程如下:
- 客户端浏览器发送请求给 Web 服务器,请求特定
URL
的资源。 - Web 服务器接收到请求,检查资源是否存在缓存中。如果存在缓存中,服务器将返回缓存的资源给客户端浏览器,否则将从磁盘或者数据库中读取资源生成响应数据返回给客户端浏览器。
- 客户端浏览器接收到响应,将响应内容和与之关联的
HTTP
头部信息(如过期时间、Cache-Control
等)存储在本地缓存中。 - 当客户端浏览器再次请求同样的 URL 时,浏览器会首先检查本地缓存中是否存在响应内容的副本。如果存在,浏览器将直接读取缓存中的响应数据并将其展示给用户,否则浏览器会重新发送一次网络请求,获取新的响应内容。
官方介绍: Web 缓存是
可以自动保存常见文档副本的 HTTP 设备
。当 Web 请求抵达缓存时, 如果本地有“已缓存的”副本,就可以从本地存储设备而不是原始服务器中提取这个文档。举个例子:
看图,问题就是出在,服务器需要处理http的请求,并且http去传输数据,需要带宽,带宽是要钱买的啊。而我们缓存,就是为了让服务器不去处理这个请求,客户端也可以拿到数据。
注意,我们的缓存主要是针对html,css,img等静态资源,常规情况下,我们不会去缓存一些动态资源,
因为缓存动态资源的话,数据的实时性就不会不太好,所以我们一般都只会去缓存一些不太容易被改变的静态资源。
3. HTTP 缓存策略
Expires
:缓存过期时间,在过期时间之前直接使用缓存(缺点是无法保证客户端和服务器时间的一致性)。Cache-Control
:跑得快,如no-cache
(禁止缓存)、max-age
(缓存的最长时间)、public
(允许所有的缓存服务器进行缓存)、private
(只允许客户端缓存,不允许代理服务器进行缓存)。Last-Modified/If-Modified-Since
:缓存有效性依据最后修改时间,如Last-Modified
表示缓存的最后修改时间,如果此后资源有更新,资源的 Last-Modified 值会随之发生变化,而If-Modified-Since
首部则可以将之前缓存资源的 Last-Modified 值带入请求中,如果服务器判断 Last-Modified 值与当前资源的最后修改时间一致,则说明没有发生变化,直接返回304
状态码,浏览器从其缓存中加载资源。ETag/If-None-Match
:缓存有效性依据文件摘要,即ETag
(比 Last-Modified 更精确,但相对于Last-Modified
更消耗性能),如ETag
表示缓存资源的唯一标识,当资源发生变化时,ETag 的值会发生改变,而If-None-Match
首部可以将之前缓存资源的 ETag 值带入请求中,如果服务器判断 ETag 值与当前资源的 ETag 值不一致,则说明资源已经发生变化,直接返回304
状态码,浏览器从其缓存中加载资源。
4. HTTP 缓存的优点和缺点
a、优点:
- 减少网络请求:通过使用
HTTP
缓存,可以避免多次请求同一资源的情况,减少网络负载,提高网站性能。 - 加快页面加载速度:从缓存中加载资源速度比网络请求快得多,这能够显著减少页面的加载时间,提高用户体验。
- 降低服务器负载:通过利用 HTTP 缓存,可以减少
Web
服务器的负载,从而降低服务器压力。
b、缺点:
- 缓存策略难以控制:浏览器的缓存策略对缓存内容的控制相对较弱,Web 开发者只能通过
HTTP 头部
设置特定的缓存策略,但无法控制最终的缓存结果
,这在一定程度上也增加了缓存调试的难度。 - 客户端缓存不可靠:由于客户端缓存是保存在用户的计算机上,因此数据的可靠性和安全性无法保证,在某些情况下可能会导致
数据冲突或数据泄露
的问题。 - 缓存管理和调试困难:为了保持缓存数据的一致性和有效性,本地缓存数据需要经常进行清理和更新,这就需要进行
缓存管理和调试工作
,这可能会对开发者造成额外的压力。
总体来说,HTTP 缓存是一种非常有效的 Web 性能优化策略,可以显著提高网站的性能和用户体验,但同时也需要注意其缺点和安全性问题,以确保网站的可靠性和安全性。
四、强制缓存 和 协商缓存详解
1、强制缓存
强制缓存 是
优先从浏览器本地缓存中获取资源
,而不是向服务器发送请求。强制缓存的过程主要通过设置HTTP 头信息
中的缓存控制字段(Cache-Control
、Expires
等)来实现。如果客户端缓存数据未过期(Cache-Control max-age 没有到期或 Expires 没有过期),浏览器可以直接从本地缓存中获取数据,而不必再向服务器请求数据,从而减少了网络请求,也降低了服务器压力。
a、优点:
强制缓存可以大大提升网站性能,因为它可以避免不必要的网络请求,减少服务器负担,并提高了用户体验。
b、缺点:
强制缓存相对灵活性较差,无法在缓存时间内及时更新缓存内容,如果资源更新频繁,可能会导致浏览器拿到过期的缓存数据,从而引发页面展示错误
等问题。
c、使用场景:
对于不经常更新的资源数据,比如静态图片、样式、脚本等,可以采用强制缓存。
d、使用原理:
在发送 HTTP 请求时,请求头(Header
)中添加 Cache-Control
和 Expires
字段,服务器在发送响应时,设置 Cache-Control 和 Expires 字段的值,即控制浏览器读取本地缓存数据的时间
。
e、基于Expires字段实现的强缓存
基于 Expires
字段实现的强缓存是 HTTP 协议中 较为传统 的缓存策略之一,早期
广泛应用于静态资源的缓存。Expires
字段被用来 指定浏览器缓存该资源的过期时间,如果缓存中有该资源,并且该资源未过期,则浏览器直接从本地缓存中读取该资源,而不必向服务器发送请求,这样就可以大幅减少网络请求次数,从而提升页面的响应性能。
具体的实现方式:服务端在响应头
中设置 Expires
字段的值,指定该资源的缓存过期时间。该字段的值是一个时间戳
,表示缓存数据的过期时间,当浏览器发出请求时,会 检查该字段的值与当前时间的关系,如果过期时间大于当前时间,则表明该资源还在缓存有效期内,浏览器则会直接从本地缓存中读取该资源文件。如果过期时间小于当前时间,则浏览器会将该请求发送到服务器进行资源文件更新。
下面是一个设置 Expires 字段的 示例:
Expires: Wed, 26 May 2021 08:00:00 GMT
该示例设置了资源的缓存过期时间为 2021 年 5 月 26 日 08:00:00 GMT。也可以换一种形式设置,如下:
但是,Expires已经被废弃了。对于强缓存来说,Expires已经不是实现强缓存的首选!!!!!
因为Expires 判断强缓存是否过期的机制 是:获取本地时间戳
,并对先前拿到的资源文件中的Expires字段的时间做比较。来判断是否需要对服务器发起请求。这里有一个巨大的漏洞:“如果我本地时间不准咋办?”
是的,Expires过度依赖本地时间,由于本地时间和服务器时间可能不一致,导致缓存效果不稳定,如果本地与服务器时间不同步,就会出现资源无法被缓存或者资源永远被缓存的情况。所以,Expires字段几乎不被使用了。现在的项目中,我们并不推荐使用Expires,强缓存功能通常使用cache-control
字段来代替Expires字段。
f 、基于 Cache-Control 字段实现的强缓存(代替Expires的强缓存实现方法)
基于 Cache-Control
字段实现的强缓存是 目前比较常用 的一种缓存方案,基于 Cache-Control 实现的强制缓存控制能够应对 Expires 指定方式带来的客户端时间和服务端时间不一致问题,缓存控制更加先进和可靠。
Cache-Control
字段是一个 指令集 ,包含多个子指令,常用的、与强制缓存相关的指令有以下 6 种:
-
public
:响应可以被任何缓存所缓存。 -
private
:响应只能被单个用户缓存(比如浏览器页面缓存)。 -
no-cache
:强制所有缓存都不能缓存该资源,需要使用协商缓存验证策略。 -
no-store
:禁止缓存保存任何资源。 -
max-age=<seconds>
:决定客户端资源在该秒数内缓存有效。 -
s-maxage
:决定代理服务器缓存的时长。
常见的值如下:
Cache-Control: public
:响应可以被任何缓存所缓存。Cache-Control: private
:响应只能被单个用户缓存(比如浏览器页面缓存)。Cache-Control: no-cache
:缓存不能直接使用缓存,需向源站发起请求进行新鲜度校验。Cache-Control: no-store
:缓存不会保存请求或响应的任何数据。Cache-Control: max-age=<seconds>
:缓存内容将在指定的秒数内保持新鲜,比如 604800 表示一周。
下面是通过设置 Cache-Control 头来实现缓存的 示例:
res.writeHead(200,{'Cache-Control':'max-age=3600, public'
});
该示例中,max-age
指令设置资源文件的缓存时间,单位为秒,public
指令表示该资源文件可以被任何缓存进行缓存。表示 该资源文件可以在 3600 秒内被缓存(1小时),并且可以被其他缓存保存。当需要读取该资源文件时,会首先判断缓存是否过期,如果没有过期,则直接从本地缓存中读取该文件,否则会向服务器请求该文件。
Cache-Control:max-age=N
,N就是需要缓存的秒数。从第一次请求资源的时候开始,往后N秒内,资源若再次请求,则直接从磁盘(或内存中读取),不与服务器做任何交互。
Cache-control中因为max-age后面的值是一个 滑动时间,从服务器第一次返回该资源时开始倒计时。所以也就不需要比对客户端和服务端的时间,解决了Expires
所存在的巨大漏洞。
需要注意的是: 当 同时设置 了 Expires 和 Cache-Control时,
Cache-Control 的优先级高于 Expires
,
因此如果使用了 Cache-Control,建议不再使用 Expires。
2. 协商缓存
协商缓存 是一种比较灵活的缓存设置方式,可以在服务器更新资源时更及时地通知客户端更新本地缓存。是在客户端向服务器端请求某个资源时,会
根据某些条件(如 Last-Modified、ETag 等)判断服务器发送的资源是否已经过期
,如果未过期,则使用缓存,否则则向服务器重新发送请求来获取最新的资源数据。协商缓存的实现方式主要有两种:Last-Modified / If-Modified-Since
和ETag / If-None-Match
。
a、优点:
协商缓存可以更及时地更新缓存
,因为服务器可以根据资源内容的修改情况灵活地调整缓存时间,同时避免了强制缓存可能会出现的未及时更新的问题。
b、缺点:
相对于强制缓存,协商缓存需要进行更多的网络请求,降低了页面性能,从而可能影响用户体验。
c、使用场景:
对于频繁更新的数据
,比如新闻、文章、动态数据等,可以采用协商缓存机制。
d、使用原理:
通过设置 ETag
和 Last-Modified
字段,告诉浏览器资源的状态,当浏览器再次请求该资源时,会在请求头
中携带上次请求中的 ETag 或 Last-Modified 信息,服务器根据这些信息来判断资源是否需要更新,如果需要更新,则返回最新的资源数据,否则返回 304 Not Modified
,浏览器继续使用本地缓存数据。
e、基于Last-Modified / If-Modified-Since 的协商缓存
基于Last-Modified / If-Modified-Since
的协商缓存是HTTP协议中常见的一种缓存策略。该策略是 通过比较客户端浏览器上缓存的资源的最后修改时间(Last-Modified)与服务器上当前资源的最后修改时间(Last-Modified)是否一致来判断是否要用缓存。
具体实现步骤如下:
- 当浏览器 第一次请求资源 时,服务器会在响应头中添加
Last-Modified
字段,表示该资源最后的修改时间,并且设置Cache-control:no-cache
。格式如下:
注意圈出来的三行。
- 第一行,读出修改时间。
- 第二行,给该资源响应头的
last-modified
字段赋值修改时间 - 第三行,给该资源响应头的
Cache-Control
字段值设置为:no-cache
.(上文有介绍,Cache-control:no-cache的意思是跳过强缓存校验,直接进行协商缓存。)
- 当浏览器 再次请求该资源 时,会在请求头中加入
If-Modified-Since
字段,值就是上一次请求得到的 Last-Modified 字段的值,格式如下:
那么之后每次对该资源的请求,都会带上
If-Modified-Since
这个字段,而务端就需要拿到这个时间并再次读取该资源的修改时间,让他们两个做一个比对来决定是读取缓存还是返回新的资源。
-
服务器接收到请求后,会把资源的 最后修修改时间 与请求头中的
If-Modified-Since
值进行比较,如果它们相等,说明资源未被修改过,返回304
状态码。 -
如果资源已经被修改过,则服务器会 返回新的资源,并 更新资源的最后修改时间。此时,响应头就会随着资源的更新,重新设置
Last-Modified
字段。
整体流程代码:
需要注意的是: 服务器返回的时间戳应该是
GMT
格式(格林威治标准时间
,世界上实际存在的时间标准),而不应该是本地的时区时间。因为不同地区的时区有差异,假如使用本地的时区时间,客户端和服务器的时间可能不一致,导致缓存判断出错。
另外,如果忽略了一些细节问题,使用
Last-Modified / If-Modified-Since
的缓存机制是比较简单的。缺点
是Last-Modified只能保证资源的最后修改时间是否发生过改变,但无法检测到资源内容是否有改变的情况 :
- 因为是更具文件修改时间来判断的,所以,在文件内容本身不修改的情况下,依然有可能更新文件修改时间(比如 修改文件名再改回来),这样,就有可能文件内容明明没有修改,但是缓存依然失效了。
- 当 文件在极短时间内完成修改 的时候(比如几百毫秒)。因为文件修改时间记录的最小单位是秒,所以,如果文件在几百毫秒内完成修改的话,文件修改时间不会改变,这样,即使文件内容修改了,依然不会返回新的文件。
所以为了解决这个问题,就出现了
基于ETag的协商缓存
f、基于ETag的协商缓存
基础ETag的协商缓存 是通过客户端发起的If-None-Match
请求头来与服务端进行交互的一种缓存策略。
ETag
就是将原先协商缓存的比较时间戳
的形式修改成了比较文件指纹
。是一个用于表示资源的标识符,由服务器在响应请求时返回给客户端。
文件指纹:根据文件内容计算出的唯一哈希值。文件内容一旦改变则指纹改变。
下面是该策略的详细实现过程:
-
服务器在响应资源请求时在响应头中添加
ETag
字段,格式如下:ETag: "xxxxx"
其中"xxxxx"是一个唯一的字符串,用于标识该资源的版本信息。
-
当浏览器再次请求该资源时,会在请求头中加入
If-None-Match
字段,值就是上一次请求得到的ETag
字段的值,格式如下:If-None-Match: "xxxxx"
-
服务器收到请求后,会把当前资源的 ETag 值与请求头中的 If-None-Match 值进行比较,如果两者相等,则说明此时资源未改变,返回 304 Not Modified 状态码,客户端可以直接使用缓存数据;否则说明资源已经发生了改变,服务器将返回新的资源,并更新 ETag 值。
简单来说,如果服务器检测到浏览器发送了 If-None-Match 请求头,那么它会将浏览器发送的 ETag 值与当前资源的 ETag 值进行比对,一致则直接使用缓存,否则返回新的资源。
需要注意的是,和Last-Modified / If-Modified-Since有类似的问题,当ETag的值比较复杂,且需要花费较多的计算时间来生成的时候,会影响服务器的性能。因此,对于某些资源,可以使用较简单的方式来返回ETag的值,比如只使用文件的大小信息 作为ETag值,这样 可以达到一定的ETag值的唯一性,并且减少了计算成本。