中高级前端工程师都需要熟悉的技能--前端缓存

article/2025/3/15 12:38:13

前言

web缓存是高级前端工程师必修技能。是我们变成大牛过程中绕不开的知识点。

文章会尽量用通俗易懂的言语来细说web缓存的概念和用处。

本期文章的大纲是

  1. 什么是web缓存(前端缓存)

  2. 缓存可以解决什么问题?他的缺点是什么?

  3. 强制缓存原理讲解

    3.1.基于Expires字段实现的强缓存

    3.2.基于Cache-control实现的强缓存

  4. 协商缓存原理讲解

    4.1.基于last-modified实现的协商缓存

    4.2.基于ETag实现的协商缓存

什么是web缓存?

web缓存主要指的是两部分:浏览器缓存和http缓存。

其中http缓存是web缓存的核心,是最难懂的那一部分,也是最重要的那一部分。

浏览器缓存:比如,localStorage,sessionStorage,cookie等等。这些功能主要用于缓存一些必要的数据,比如用户信息。比如需要携带到后端的参数。亦或者是一些列表数据等等。

不过这里需要注意。像localStorage,sessionStorage这种用户缓存数据的功能,他只能保存5M左右的数据,多了不行。cookie则更少,大概只能有4kb的数据。不要担心,这些概念对于未来会称为前端大牛的你来说都不是什么问题,非常的简单。因为太简单,数据缓存不再这篇文章的介绍中,这里一笔带过,需要了解的小伙伴,可以移步我的另一篇文章前端新能优化篇之localStorage和sessionStorage的区别及其使用方式 - 掘金 (juejin.cn)。

这篇文章重点讲解的是:前端http缓存。

http缓存

官方介绍:Web 缓存是可以自动保存常见文档副本的 HTTP 设备。当 Web 请求抵达缓存时, 如果本地有“已缓存的”副本,就可以从本地存储设备而不是原始服务器中提取这 个文档。

举个例子↓

看图,问题就是出在,服务器需要处理http的请求,并且http去传输数据,需要带宽,带宽是要钱买的啊。而我们缓存,就是为了让服务器不去处理这个请求,客户端也可以拿到数据。

注意,我们的缓存主要是针对html,css,img等静态资源,常规情况下,我们不会去缓存一些动态资源,因为缓存动态资源的话,数据的实时性就不会不太好,所以我们一般都只会去缓存一些不太容易被改变的静态资源。

缓存可以解决什么问题?他的缺点是什么?

先说说,缓存可以解决什么问题。

  • 减少不必要的网络传输,节约宽带(就是省钱)
  • 更快的加载页面(就是加速)
  • 减少服务器负载,避免服务器过载的情况出现。(就是减载)

再说说缺点

  • 占内存(有些缓存会被存到内存中)

其实日常的开发中,我们最最最最关心的,还是"更快的加载页面";尤其是对于react/vue等SPA(单页面)应用来说,首屏加载是老生常谈的问题。这个时候,缓存就显得非常重要。不需要往后端请求,直接在缓存中读取。速度上,会有显著的提升。是一种提升网站性能与用户体验的有效策略。

http缓存又分为两种两种缓存,强制缓存协商缓存,我们来深度剖析一下强制缓存和协商缓存各自的优劣以及他们的使用场景以及使用原理

http缓存流程图↓

强制缓存

强制缓存,我们简称强缓存。

从强制缓存的角度触发,如果浏览器判断请求的目标资源有效命中强缓存,如果命中,则可以直接从内存中读取目标资源,无需与服务器做任何通讯。

基于Expires字段实现的强缓存

在以前,我们通常会使用响应头的Expires字段去实现强缓存。如下图↓

Expires字段的作用是,设定一个强缓存时间。在此时间范围内,则从内存(或磁盘)中读取缓存返回。

比如说将某一资源设置响应头为:Expires:new Date("2022-7-30 23:59:59");

那么,该资源在2022-7-30 23:59:59 之前,都会去本地的磁盘(或内存)中读取,不会去服务器请求。

但是,Expires已经被废弃了。对于强缓存来说,Expires已经不是实现强缓存的首选。

因为Expires判断强缓存是否过期的机制是:获取本地时间戳,并对先前拿到的资源文件中的Expires字段的时间做比较。来判断是否需要对服务器发起请求。这里有一个巨大的漏洞:“如果我本地时间不准咋办?”

是的,Expires过度依赖本地时间,如果本地与服务器时间不同步,就会出现资源无法被缓存或者资源永远被缓存的情况。所以,Expires字段几乎不被使用了。现在的项目中,我们并不推荐使用Expires,强缓存功能通常使用cache-control字段来代替Expires字段。

没想到吧,整半天,这个属性是废的。

基于Cache-control实现的强缓存(代替Expires的强缓存实现方法)

Cache-control这个字段在http1.1中被增加,Cache-control完美解决了Expires本地时间和服务器时间不同步的问题。是当下的项目中实现强缓存的最常规方法。

Cache-control的使用方法页很简单,只要在资源的响应头上写上需要缓存多久就好了,单位是秒。比如↓

//往响应头中写入需要缓存的时间
res.writeHead(200,{'Cache-Control':'max-age=10'
});
复制代码

下图的意思就是,从该资源第一次返回的时候开始,往后的10秒钟内如果该资源被再次请求,则从缓存中读取。 

Cache-Control:max-age=N,N就是需要缓存的秒数。从第一次请求资源的时候开始,往后N秒内,资源若再次请求,则直接从磁盘(或内存中读取),不与服务器做任何交互。

Cache-control中因为max-age后面的值是一个滑动时间,从服务器第一次返回该资源时开始倒计时。所以也就不需要比对客户端和服务端的时间,解决了Expires所存在的巨大漏洞。

Cache-controlmax-ages-maxageno-cacheno-storeprivatepublic这六个属性。

  • max-age决定客户端资源被缓存多久。
  • s-maxage决定代理服务器缓存的时长。
  • no-cache表示是强制进行协商缓存。
  • no-store是表示禁止任何缓存策略。
  • public表示资源即可以被浏览器缓存也可以被代理服务器缓存。
  • private表示资源只能被浏览器缓存。

no-cache和no-store

no_cacheCache-control的一个属性。它并不像字面意思一样禁止缓存,实际上,no-cache的意思是强制进行协商缓存。如果某一资源的Cache-control中设置了no-cache,那么该资源会直接跳过强缓存的校验,直接去服务器进行协商缓存。而no-store就是禁止所有的缓存策略了。

注意,no-cache和no-store是一组互斥属性,这两个属性不能同时出现在Cache-Control中。

public和private

一般请求是从客户端直接发送到服务端,如下↓

但有些情况下是例外的:比如,出现代理服务器,如下↓

publicprivate就是决定资源是否可以在代理服务器进行缓存的属性。

其中,public表示资源在客户端和代理服务器都可以被缓存。

private则表示资源只能在客户端被缓存,拒绝资源在代理服务器缓存。

如果这两个属性值都没有被设置,则默认为private

注意,publicprivate也是一组互斥属性。他们两个不能同时出现在响应头的cache-control字段中。

max-age和s-maxage

max-age表示的时间资源在客户端缓存的时长,而s-maxage表示的是资源在代理服务器可以缓存的时长。

在一般的项目架构中max-age就够用。

s-maxage因为是代理服务端的缓存时长,他必须和上面说的public属性一起使用(public属性表示资源可以在代理服务器中缓存)。

注意,max-ages-maxage并不互斥。他们可以一起使用。

那么,Cache-control如何设置多个值呢?用逗号分割,如下↓

Cache-control:max-age=10000,s-maxage=200000,public

强制缓存就是以上这两种方法了。现在我们回过头来聊聊,Expires难道就一点用都没有了吗?也不是,虽然Cache-control是Expires的完全替代品,但是如果要考虑向下兼容的话,在Cache-control不支持的时候,还是要使用Expires,这也是我们当前使用的这个属性的唯一理由。

协商缓存

温馨提示:协商缓存的内容会有一点点绕。需要仔细阅读。

基于last-modified的协商缓存

基于last-modified的协商缓存实现方式是:

  1. 首先需要在服务器端读出文件修改时间,
  2. 将读出来的修改时间赋给响应头的last-modified字段。
  3. 最后设置Cache-control:no-cache

三步缺一不可。

如下图↓

注意圈出来的三行。

第一行,读出修改时间。

第二行,给该资源响应头的last-modified字段赋值修改时间

第三行,给该资源响应头的Cache-Control字段值设置为:no-cache.(上文有介绍,Cache-control:no-cache的意思是跳过强缓存校验,直接进行协商缓存。)

还没完。到这里还无法实现协商缓存

当客户端读取到last-modified的时候,会在下次的请求标头中携带一个字段:If-Modified-Since

而这个请求头中的If-Modified-Since就是服务器第一次修改时候给他的时间,也就是上图中的

 这一行。

那么之后每次对该资源的请求,都会带上If-Modified-Since这个字段,而务端就需要拿到这个时间并再次读取该资源的修改时间,让他们两个做一个比对来决定是读取缓存还是返回新的资源。

如图↓

这样,就是协商缓存的所有操作了。

看到这里,有些小伙伴可能有些迷糊了。

没关系,我们用一张图来解释下协商缓存。

使用以上方式的协商缓存已经存在两个非常明显的漏洞。这两个漏洞都是基于文件是通过比较修改时间来判断是否更改而产生的。

1.因为是更具文件修改时间来判断的,所以,在文件内容本身不修改的情况下,依然有可能更新文件修改时间(比如修改文件名再改回来),这样,就有可能文件内容明明没有修改,但是缓存依然失效了。

2.当文件在极短时间内完成修改的时候(比如几百毫秒)。因为文件修改时间记录的最小单位是秒,所以,如果文件在几百毫秒内完成修改的话,文件修改时间不会改变,这样,即使文件内容修改了,依然不会 返回新的文件。

为了解决上述的这两个问题。从http1.1开始新增了一个头信息,ETag(Entity 实体标签)

又来新东西了,兄弟们顶住

基础ETag的协商缓存

不用太担心,如果你已经理解了上面比较时间戳形式的协商缓存的话,ETag对你来说不会有难度。

ETag就是将原先协商缓存的比较时间戳的形式修改成了比较文件指纹

文件指纹:根据文件内容计算出的唯一哈希值。文件内容一旦改变则指纹改变。

我们来看一下流程↓

1.第一次请求某资源的时候,服务端读取文件并计算出文件指纹,将文件指纹放在响应头的etag字段中跟资源一起返回给客户端。

2.第二次请求某资源的时候,客户端自动从缓存中读取出上一次服务端返回的ETag也就是文件指纹。并赋给请求头的if-None-Match字段,让上一次的文件指纹跟随请求一起回到服务端。

3.服务端拿到请求头中的is-None-Match字段值(也就是上一次的文件指纹),并再次读取目标资源并生成文件指纹,两个指纹做对比。如果两个文件指纹完全吻合,说明文件没有被改变,则直接返回304状态码和一个空的响应体并return。如果两个文件指纹不吻合,则说明文件被更改,那么将新的文件指纹重新存储到响应头的ETag中并返回给客户端

代码图例↓

流程示例图↓

从校验流程上来说,协商缓存的修改时间比对和文件指纹比对,几乎是一样的。

ETag也有缺点

  • ETag需要计算文件指纹这样意味着,服务端需要更多的计算开销。。如果文件尺寸大,数量多,并且计算频繁,那么ETag的计算就会影响服务器的性能。显然,ETag在这样的场景下就不是很适合。

  • ETag有强验证和弱验证,所谓将强验证,ETag生成的哈希码深入到每个字节。哪怕文件中只有一个字节改变了,也会生成不同的哈希值,它可以保证文件内容绝对的不变。但是,强验证非常消耗计算量。ETag还有一个弱验证,弱验证是提取文件的部分属性来生成哈希值。因为不必精确到每个字节,所以他的整体速度会比强验证快,但是准确率不高。会降低协商缓存的有效性。

值得注意的一点是,不同于cache-controlexpires的完全替代方案(说人话:能用cache-control就不要用expiress)。ETag并不是last-modified的完全替代方案。而是last-modified的补充方案(说人话:项目中到底是用ETag还是last-modified完全取决于业务场景,这两个没有谁更好谁更坏)。

总结一下

  • http缓存可以减少宽带流量,加快响应速度。

  • 关于强缓存,cache-controlExpires的完全替代方案,在可以使用cache-control的情况下不要使用expires

  • 关于协商缓存,etag并不是last-modified的完全替代方案,而是补充方案,具体用哪一个,取决于业务场景。

  • 有些缓存是从磁盘读取,有些缓存是从内存读取,有什么区别?答:从内存读取的缓存更快。

  • 所有带304的资源都是协商缓存,所有标注(从内存中读取/从磁盘中读取)的资源都是强缓存。

谢谢你能那么有耐心的看完。祝你好运。

 

 

  程序员面试题库分享

1、前端面试题库 (面试必备)            推荐:★★★★★

地址:前端面试题库

2、前端技术导航大全      推荐:★★★★★

地址:前端技术导航大全

3、开发者颜色值转换工具   推荐:★★★★★

地址 :开发者颜色值转换工具


http://chatgpt.dhexx.cn/article/BP44hQ7B.shtml

相关文章

ovo svm_反思我在OVO担任远程产品设计实习生的时间

ovo svm In a quiet bedroom accompanied only by the low humming of my laptop fan, I sat before a Google Hangouts meeting, and got to know my colleagues for the first time, unaware of the joy of a ride that was waiting for me at OVO Design. 在一个安静的卧室里…

反思最近这些时日的荒废

为什么80%的码农都做不了架构师?>>> 算是一时兴起,最近lol排位已经将自己的折磨的不成人样。闲了这么久,是时候找份工作了。最近一直没敢跟家里人打电话,实在不知道该说些什么,一开口便是谎言。是否自己真的…

团队愿景_周一的愿景,每日的成果,周五的远程团队管理反思

团队愿景 My friend J.D. Meier has an amazing blog called Sources of Insight and hes written a fantastic book called Getting Results the Agile Way. You can buy his book on Amazon (its free on Kindle Unlimited!). I put J.D. up there with David Allen and Step…

WPBeginner年满10岁-反思,更新和WordPress赠品(奖金124,000美元以上)

Wow, it’s the tenth fourth. Today, WPBeginner is officially 10 years old — feels unreal to type this! 哇,是第十位 今天,WPBeginner正式成立了10岁-键入此图标感到不切实际! Like every year, I want to take a few minutes and d…

误泄露公司代码、疫情期间被裁,一个“菜鸟”程序员的生存日记

作者 | Adam Hughes 译者 | Sambodhi 策划 | Tina 编辑|燕珊 “我是如何从每一次失败中成长起来的。” 身为程序员,我们往往都了解大神级程序员的故事。比如很小就开始编程,在 11 岁时就创建了第一家能盈利的网站,16 岁上大学、17…

失败需要反思

2019独角兽企业重金招聘Python工程师标准>>> 公司的第一款游戏是抄袭的COC,然而我加入进去的时候开发已过半,进入之后主要是参与一些新的系统与玩法的开发,在我加入到这个项目之后还开发了大概半年时间,据说此前已经开…

CTF笔记 个人HNCTF反思(部分题目)

文章目录 [WEEK2]easy_include自己的胡思乱想WP [WEEK2]easy_unserexp [WEEK2]easy_sqlWP [WEEK2]ez_SSTIPAYLOAD [WEEK4]pop子和pipi美总结 怎么说呢,这次充分感觉到了自己的无能,可能因为在比赛马上结束的时候加入,让我没心思慢慢思考&…

一名大学毕业生的反思_反思我大学毕业时的软件工程师的第一年

一名大学毕业生的反思 Note: This post is mainly targeted towards students who are about to graduate or have already graduated and are preparing to start their new full-time job. Some of the examples used are specific to my experience as a New Grad Software …

华清远见嵌入式培训_第二周回顾与反思

目录 前言 周一 一、switch..case 1.1 注意事项 1.2 使用练习 二、循环控制语句 2.1 使用goto实现循环 2.2 while循环 2.3 do..while 循环 2.4 for 循环 2.5 死循环 2.6 辅助控制关键字 周二 一、数组 1.1 一维数组 1.2 数组越界问题 1.3 二维数组 1.4 编码练…

让计算机开口说话教学反思,英语教学反思(合集15篇)

英语教学反思(合集15篇) 身为一位优秀的老师,课堂教学是我们的工作之一,借助教学反思我们可以快速提升自己的教学能力,我们该怎么去写教学反思呢?以下是小编为大家收集的英语教学反思,希望能够帮助到大家。 英语教学反…

华清远见嵌入式培训_第五周回顾与反思

前言 这是在华清学习的第五个周末,这一周主要学习的是数据结构。老师说数据结构是一门非常庞大的学科,单单是数据结构里的一个小分支,单拎出来一周都未必可以学透,因此这周的数据结构课程里更多的是思维方向的学习,学习…

利用Python识别txt文本并根据其内容进行文件分类

事情是这样的,有一个图片数据集需要根据分成很多类以便于给其设置标签,但所有的图片都在一个文件里,另外又给了个.txt文件,其中每行都是对应图片的类别。例如第1行对应的第0001.jpg是第14类(每个类都有多张图片&#x…

C# 批量修改文件名称

目的 对文件夹中所有文件名实现批量修改(添加新字符) 思路 获取文件夹路径获取文件夹中所有文件的文件名对文件名进行批量修改 方法 窗口设计 获取文件夹路径 使用FolderBrowserDialog控件获取文件夹路径,并用Directory.Exists方法对路径…

Unity Shader入门精要之Unity 提供的内置文件和变量

Unity系列文章目录 文章目录 Unity系列文章目录前言5.3.1 内置的包含文件5.3.2 内置的变量 二、Unity 提供的Cg/HLSL 语义5.5 程序员的烦恼:Debug5.6 小心:渲染平台的差异5.7 Shader 整洁之道参考 前言 上一节讲述了如何在Unity 中编写一个基本的顶点/片…

《python数学实验与建模》(10)图论模型

10.1 写出图10.20所示非赋权无向图的关联矩阵和邻接矩阵 绘制图 import networkx as nx import pylab as plt import numpy as np Anp.zeros((6,6)) List[(1,2),(1,4),(2,3),(3,4),(3,5),(3,6),(4,5),(4,6),(5,6)] for i in List:A[i[0]-1,i[1]-1]1 Gnx.Graph(A) posnx.spring…

2021年美国数学建模C题的数据处理

2021年美国数学建模C题的数据处理 C题数据分类存放部分批量提取图像数据转化jpg图像格式 C题数据分类存放部分 在拿到C题的数据后,避让要做的一个事情是图像数据的分类。根据2021MCM_ProblemC_ Images_by_GlobalID表格中,可以将图片和ID号对应起来&…

Unity Shader入门精要笔记(五):其他数学相关介绍

本系列文章由Aimar_Johnny编写,欢迎转载,转载请标明出处,谢谢。 http://blog.csdn.net/lzhq1982/article/details/73747162 前两篇介绍了Unity Shader的主要数学部分,书上还有些相关的数学介绍,将在这篇做最后的总结。…

2020年数维杯国际大学生数学建模B题股票价格的混沌模型求解全过程文档及程序

2020年数维杯国际大学生数学建模 B题 股票价格的混沌模型 原题再现: 上市公司股价的变化可以直接反映上市公司的经营状况和市场的认可度。股票价格的建模和预测一直是一个难题。最重要的因素是股票价格既有趋势因素又有随机因素。因此,股票市场是一个非…

ugpost_tcl文件

########################## TCL Event Handlers ########################## b.tcl - 3_axis_mill 这是 3 轴铣床。 Created by dp Wednesday, November 06, 2019 8:52:33 AM China Standard Time with Post Builder version 10.0.3. #####################################…

数据结构课设 (快餐店 POS 机计费系统、成绩分析、算术表达式)

目录 快餐店 POS 机计费系统 学生成绩分析系统 算术表达式 参考文献 快餐店 POS 机计费系统 【任务描述】 校园快餐店一共出售三大类食品:饮料,主食,小食品。设计一个快餐店的 POS 机计费系统, 对快餐店的食品信息、销售信息…