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

前言

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的区别及其使用方式。

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

http缓存

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

举个例子↓

image.png

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

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

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

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

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

再说说缺点

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

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

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

http缓存流程图↓

image.png

强制缓存

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

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

基于Expires字段实现的强缓存

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

image.png

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字段。

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

image.png

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

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

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

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

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

image.png

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

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

image.png

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

image.png

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

三步缺一不可。

如下图↓

image.png

注意圈出来的三行。

第一行,读出修改时间。

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

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

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

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

image.png

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

image.png

这一行。

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

如图↓

image.png

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

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

image.png

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

image.png

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

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

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

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

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

image.png

基础ETag的协商缓存

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

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

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

我们来看一下流程↓

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

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

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

代码图例↓

image.png

流程示例图↓

image.png

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

ETag也有缺点

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

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

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

追加

有掘友说👇

image.png

我来补足一下。

如何设置缓存

从前端的角度来说:

你什么都不用干,缓存是缓存在前端,但实际上代码是后端的同学来写的。如果你需要实现前端缓存的话啊,通知后端的同学加响应头就好了。

从后端的角度来说

请参考文章,虽然文章里的后端是使用node.js写的,但我写了详细的注释。对于后端的同学来说。应该不难看懂。

哪些文件对应哪些缓存

这个,我确实忘了说。哈哈哈。

有哈希值的文件设置强缓存即可。没有哈希值的文件(比如index.html)设置协商缓存

为什么有哈希值的文件设置强缓存

image.png

image.png

这是我打完包之后的css文件。大家是否注意到。我划了红线的部分。明显,这绝不是我的文件名。这串和乱码一样的字符串叫哈希值。每次打包之后都会生产一串新的哈希值并追加到我们的文件名中。哈希值是打包后的文件名的一部分。

我们给css设置强缓存,哪怕缓存1W年。只要我们重新打包,生产新的哈希值。那么文件名就更改了。对于机器来说,更改了文件名的文件,就是一个新的文件。

举个例子👇

比如,有一个css文件a1

第一次打包a1.css文件追加哈希值变成了 a1.aaaaa.css,我们给a1.aaaaa.css设置了强缓存1W年。

然后项目改动,我们又打包了一次。打包后生产新的哈希值,a1.aaaaa.css变成了a1.bbbbb.css文件。那么当我们第一次访问a1.bbbbb.css文件的时候是不会被缓存。因为1W年的缓存是给a1.aaaaa.css文件做的。关我a1.bbbbb.css文件什么事?这样我们也就能拿到最新的改动。

其他可以被webpack生成哈希值的文件同理。

为什么index.html使用协商缓存

既然img/css这些文件都可以用强缓存。通过更改文件名的方式来获取最新的数据,为什么我堂堂index.html就要用协商呢?

我给大家看个图

image.png

因为一般情况下,index.html是不会设置哈希值的。(具体得看自己项目下的dist文件夹)

注意:哈希值是需要webpack生成的。不是天生的。不过有些框架会自带(比如我使用的umi.js),设置缓存前务必看下自己的dist文件。因为如果没有配置的话,你可能所有文件都不带哈希值。

总结一下

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

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

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

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/753100.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构 之 七大排序 (持续更新ing...)

下面算法编写的均是按照由小到大顺序进行排序版本 选择排序 思想: 每次遍历待排序元素的最大下标,与待排序元素中最后一个元素交换位置(此时需要设置一个临时变量来存放下标) 时间复杂度--O(n^2)空间复杂度--O(1)稳定性--不稳定 代…

TCP - 传输控制协议

TCP - 传输控制协议 是一种面向连接的可靠传输协议。 特点: TCP是面向连接(虚连接)的传输层协议。 每一条TCP连接有且只能有两个端点。 可靠、有序、无丢弃和不重复。 TCP协议提供全双工通讯。 发送缓存 存放发送方TCP准备发送的数据。T…

FDMC8200中文资料PDF数据手册引脚图参数功能介绍概述参数规格参数产品手册

产品概述: 此器件在一个双 Power 33 (3 mm X 3 mm MLP) 封装中包括了两个特制的 N 沟道 MOSFET。 开关节点已经内部连接,可实现同步降压转换器的轻松布置和布线。 控制 MOSFET (Q1) 和同步 MOSFET (Q2) 可提供最佳功率效率。 产品特性: Q1…

计算机一级word 文字处理理论+实操试题

计算机一级word 文字处理理论实操试题 单选题: 1、在Word编辑状态下,要将另一文档的内容全部添加在当前文档的当前光标处,应选择的操作是依次单击______。 A.“文件”选项卡和“打开”项 B.“文件”选项卡和“新建”项 C.“插入”选项卡…

外贸业务员的工作时间安排,抓紧收藏!

平时很多外贸业务员应该都差不多,大部分时间会用来不知所措的摸鱼,关于工作内容并无规划。今天给大家分享一个工作时间安排,赶紧码住! 销售联系潜在客户时间计划表 目标:在不给潜在客户造成压力的情况下,建立联系并推…

WEB前端作业一

作业1:生成表格 模版 <!DOCTYPE html> <html><title>作业表格一</title><body><h1><b>工商银行电子汇款单</b></h1><p><table border"1" cellspacing"0"><tr><td colspan&qu…

最小化战斗力差距——算法思路

题目链接&#xff1a;1.最小化战斗力差距 - 蓝桥云课 (lanqiao.cn) 可分析&#xff0c;把一个数组分成两组&#xff0c;求一组的最大值与另一组的最小值的差值的绝对值最小&#xff0c;可以转换为求任意两个相邻数字之间的最小插值的绝对值。 可看图示&#xff1a; package lan…

记录工作中莫名其妙的bug

1、问题&#xff1a;办公室的电脑突然除了我之外&#xff0c;都不能访问我们的线上系统了 原因&#xff1a;因为是内网&#xff0c;同事有刚刚升级了Windows11&#xff0c;配置的DNS被清了&#xff0c;还有同事换了公司的新电脑&#xff0c;还没有配DNS 位于&#xff1a;C /Win…

如何在CentOS系统部署AMH主机面板并实现无公网IP远程连接

文章目录 推荐1. Linux 安装AMH 面板2. 本地访问AMH 面板3. Linux安装Cpolar4. 配置AMH面板公网地址5. 远程访问AMH面板6. 固定AMH面板公网地址 推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击…

数据库SQLServer——插入数据

1.插入数据语法 INSERT INTO table_name(column_list) VALUES (value_list); --简写 INSERT INTO table_name VALUES (value_list);INSERT table_name VALUES (value_list); 2.实例 2.1基本形式&#xff08;不安全&#xff09; insert into 学生表01 values(李明,男,1.70) …

python实现--二叉搜索树

什么是二叉搜索树 二叉搜索树&#xff08;Binary Search Tree&#xff0c;BST&#xff09;是一种特殊类型的二叉树&#xff0c;它具有以下性质&#xff1a; 每个节点最多有两个子节点&#xff0c;分别称为左子节点和右子节点。 对于任意节点&#xff0c;其左子树中的所有节点的…

python 调用redis创建查询key

部署redis apiVersion: apps/v1 # 描述api版本&#xff0c;默认都用这个 kind: Deployment # 资源类型&#xff0c;可以配置为pod&#xff0c;deployment&#xff0c;service&#xff0c;statefulset等等 metadata: # deployment相关的元数据&#xff0c;用于描述deployment的…

LeetCode199题:二叉树的右视图(python3)

代码思路&#xff1a;深度优先搜索&#xff0c;每次总访问右子树&#xff0c;value_depth用dict存放&#xff0c;深度为索引&#xff0c;存放节点的值&#xff0c;stack从根节点[(root, 0)]开始&#xff0c;添加node和depth class Solution:def rightSideView(self, root: Opt…

测试工具分享:高效完成测试工作!

说在前头 在社会上&#xff0c;特别是技术圈&#xff0c;大家会有刻板印象&#xff1a;测试工作的含金量不高。因为大家觉得测试不重要&#xff0c;导致给测试的薪水也偏低&#xff1b;这又反向导致好的人才不想来测试行业&#xff0c;测试从业人员的平均水平、工作体现的价值…

贪心算法(算法竞赛、蓝桥杯)--线段覆盖

1、B站视频链接&#xff1a;A29 贪心算法 P1803 线段覆盖_哔哩哔哩_bilibili 题目链接&#xff1a;凌乱的yyy / 线段覆盖 - 洛谷 #include <bits/stdc.h> using namespace std;struct line{int l,r;bool operator<(line &b){return r<b.r;//重载小于号,按右端…

Windows系统安装GeoServe结合内网穿透实现公网访问本地位置信息服务

文章目录 前言1.安装GeoServer2. windows 安装 cpolar3. 创建公网访问地址4. 公网访问Geo Servcer服务5. 固定公网HTTP地址 前言 GeoServer是OGC Web服务器规范的J2EE实现&#xff0c;利用GeoServer可以方便地发布地图数据&#xff0c;允许用户对要素数据进行更新、删除、插入…

ai写作一键生成,分享6种好用的写作软件,一定要看

在写文章时&#xff0c;我们常常会遇到灵感丧失、词句不顺的情况&#xff0c;为了解决这一问题&#xff0c;小编为大家推荐几款实用的AI写作软件&#xff0c;一同来探索一下吧&#xff01; 一、爱制作AI 爱制作AI是一款专注于写作的软件&#xff0c;强大的智能数据库让它备受…

Docker进阶:离线安装docker社区版(docker-18.06.3-ce)

Docker进阶&#xff1a;离线安装docker社区版&#xff08;docker-18.06.3-ce&#xff09; 1、准备离线安装所需的文件2、传输文件至目标Linux系统3、卸载旧版Docker4、离线安装Docker1、解压上传的Docker安装包2、拷贝文件到/usr/bin目录3、将 Docker 注册为系统服务4、重新加载…

力扣203. 移除链表元素

写法1、头节点 和 后面的节点 删除规则不一致 class Solution {public ListNode removeElements(ListNode head, int val) {//如果不建虚拟头节点&#xff0c;那删头节点和删后面的节点&#xff0c;逻辑是不一样的//头节点可能连续多个命中val,所以有while&#xff0c;不是ifwh…

2684. 矩阵中移动的最大次数

说在前面 &#x1f388;不知道大家对于算法的学习是一个怎样的心态呢&#xff1f;为了面试还是因为兴趣&#xff1f;不管是出于什么原因&#xff0c;算法学习需要持续保持。 题目描述 给你一个下标从 0 开始、大小为 m x n 的矩阵 grid &#xff0c;矩阵由若干 正 整数组成。 …