【前端】前端需要知道的缓存知识总结

news/2025/1/15 20:51:05/文章来源:https://www.cnblogs.com/o-O-oO/p/18673695

引言📇

HTTP缓存是一种用于提高网站性能和减少带宽使用的技术。当用户访问一个网页时,浏览器会下载页面上的所有资源(如HTML、CSS、JavaScript等),这些资源会占用大量的带宽和时间。为了减少这些资源的加载时间,HTTP缓存机制被引入。

缓存分为强缓存和协商缓存两种,强缓存不能缓存地址栏访问的文件,协商缓存可以缓存地址栏访问的文件。

1、强缓存🛅

由服务器设置过期时间,在该时间到期之前,浏览器会直接从本地缓存中获取资源。

强缓存的实现方式有两种:Expires和Cache-Control。

1.1、Expires❤️❤️🤍🤍🤍

Expires 是 HTTP 缓存机制中实现强缓存的一种方式,它通过在响应头部中加入一个过期时间来控制缓存。

Expires 的值是一个日期,格式为:Wed, 21 Oct 2015 07:28:00 GMT

它表示该资源的过期时间。当浏览器再次请求该资源时,会判断是否在该过期时间内,如果是则直接从缓存中获取资源,否则重新向服务器请求。

要设置 Expires 头部,需要在服务器端进行配置。例如,在 Nginx 中可以使用expires指令来设置过期时间:

location / {expires 1h;
}

注意⚡⚡:由于Expires是基于客户端时间计算的,如果客户端的时间与服务器的时间不一致,则可能会影响缓存效果。

1.2、Cache-Control❤️❤️❤️🤍🤍

Cache-Control 是通过在响应头部中加入 Cache-Control 字段,并设置max-age值来表示该资源在多少秒内有效(即缓存的最大时长)。

Cache-Control响应头的最常用格式为:

Cache-Control: max-age=<seconds> // seconds 是缓存的时间,单位是秒

当浏览器请求资源得到的响应头中带有 Cache-Control 响应头时,浏览器会将该资源缓存到本地。在下一次访问该资源时,同时满足下述条件,浏览器就会使用本地资源(即缓存),而不重新去服务器请求该资源:

1、缓存时间未过期
2、URL未发生变化
3、请求头中没有 Cache-Control: no-cache 或 Pragma: no-cache 这两个信息(强制刷新会在请求头中添加 Cache-Control: no-cache)

注意⚡⚡:直接通过浏览器的地址栏访问的资源缓存会失效(跟强制刷新一样会在请求头中添加 Cache-Control: no-cache)

接下来我们通过一个简单的页面来实践一下:

目录结构,我们准备两个文件 index.js index.html ,再准备两张图片:

http
|--- index.js
|--- index.html
|--- 1.jpg
|___ 2.jpg

index.js:

提供一个 node 服务,用于返回浏览器请求的资源(index.html 和图片)

// index.js
const http = require('http')
const fs = require('fs')
const path = require('path')const server = http.createServer((req, res) => {let filePath = path.resolve(__dirname, req.url === '/' ? `index.html` : '1.jpg')res.writeHead(200, {'Content-Type': req.url === '/' ? 'text/html; charset=utf-8' : 'image/png','Cache-Control': 'max-age=86400', // 缓存一天})
const fileStream = fs.createReadStream(filePath)
return fileStream.pipe(res)
})server.on('clientError', (err, socket) => {socket.end('HTTP/1.1 400 Bad Request\r\n\r\n')
})server.listen(8080, () => {
console.log(`opened server on http://localhost:${server.address().port}`)
})

index.html

一个简单的页面,包含一张图片,因为我们会直接通过浏览器的地址栏访问 html,所以 html 的缓存策略会失效。这里我们判断缓存是否生效是通过页面中的图片去判断的。


<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hello World</title>
</head>
<body>
<h1>Hello World!</h1>
<!-- 别忘记替换成你的图片名称 -->
<img src="./1.jpg" title="123">
</body>
</html>

然后随便准备两张图片就可以了,我们在项目的跟目录使用 node index.js 来启动项目。

如下图提示,就表示启动成功,然后我们通过浏览器访问 http://localhost:8080/ 就能看到我们的页面了。

首次请求我们主要看图片的请求头跟响应头就行(因为html的缓存会失效)。

刷新页面(非强制刷新)

第二次的请求可以看到请求的 Size 变成了 memory cache,并且 Time 也变为了 0

再进一步看请求头和响应头,请求头中的 Cache-Control: no-cache 属性没有了,

并且浏览器会给出一个警告:Provisional headers are shown. Disable cache to see full headers。大致意思就是当前使用的是缓存的临时文件,禁用缓存后才可以查看完整的请求头。

验证缓存

上述的方法可能并不一定让你相信我们使用的是缓存文件,而不是重新请求的资源文件。

一开始我们准备了两张图片,现在使用的是 1.jpg,还有一个 2.jpg,我们把 1.jpg 删除了,然后把2.jpg 改名成 1.jpg,然后刷新页面(非强制刷新),就会发现虽然我们图片更改了,但是图片并不是我们后面改名的那个图片,还是之前的图片。

强制刷新后就能看到,我们替换的图片生效了,请求头中也带上了 Cache-Control: no-cache 属性。

2、协商缓存

利用浏览器和服务器之间的通信来确定是否需要重新获取资源。

协商缓存有两种实现方式:If-Modified-Since 和 ETag

当浏览器第一次请求资源时,服务器会返回该资源的 ETag 值和 Last-Modified 时间。当浏览器再次请求该资源时,它会将这些值作为请求头部的 If-Modified-Since 和 If-None-Match 字段发送给服务器。服务器会比较这些值与资源的当前状态,如果资源没有发生变化,服务器返回 304 Not Modified 响应,告诉浏览器可以使用缓存的资源。

如果资源已经发生了变化,服务器会返回新的资源,并更新 ETag 和 Last-Modified 。

2.1、If-Modified-Since ❤️❤️❤️❤️🤍

利用响应头的 Last-Modified 来设置缓存,并在下次请求的请求头中携带 If-Modified-Since 来判断该资源是否发生变化,如果发生变化则返回新的资源,并更新 Last-Modified 属性,如果没有发生变化,则返回 304 跟空的 body。

对强缓存的例子稍微修改一下

index.js


const http = require('http')
const fs = require('fs')
const path = require('path')const server = http.createServer((req, res) => {
let filePath = path.resolve(__dirname, req.url === '/' ? `index.html` : '1.jpg')
const stat = fs.statSync(filePath)
const lastModified = stat.mtime.toUTCString()
const header = {'Content-Type': req.url === '/' ? 'text/html; charset=utf-8' : 'image/png','Last-Modified': lastModified}
// 判断资源是否发生变化
if (req.headers['if-modified-since'] === lastModified) {res.writeHead(304, header)res.end()} else {res.writeHead(200, header)const fileStream = fs.createReadStream(filePath)return fileStream.pipe(res)}
})server.on('clientError', (err, socket) => {socket.end('HTTP/1.1 400 Bad Request\r\n\r\n')
})server.listen(8080, () => {
console.log(`opened server on http://localhost:${server.address().port}`)
})

首次请求

响应头携带 Last-Modified: Mon, 05 Jun 2023 08:57:17 GMT 属性,告诉浏览器这个文件需要缓存。

刷新页面(非强制刷新)

第二次请求,响应状态码变为 304,并在请求头中携带 If-Modified-Since: Mon, 05 Jun 2023 08:57:17 GMT 属性,表示浏览器使用缓存文件。

改变html文件

把 html 中 的 Hello World! 改为 Web Html,并刷新页面(非强制刷新),发现响应状态码变为 200 ,并且更新了页面和响应头的 Last-Modified 属性。

注意⚡⚡:如果资源的修改时间只精确到秒,而不是毫秒,可能会导致缓存失效。此外,如果服务器上的资源被修改了,但修改时间没有更新,也会导致缓存失效

2.2 ETag ❤️❤️❤️❤️❤️

ETag 基本上与 If-Modified-Since 一致, 利用响应头的 Etag 来设置缓存,并在下次请求的请求头中携带 if-none-match 来判断该资源是否发生变化,如果发生变化则返回新的资源,并更新 Etag 属性,如果没有发生变化,则返回 304 跟空的 body。

const http = require('http')
const fs = require('fs')
const path = require('path')
const crypto = require('crypto')const server = http.createServer((req, res) => {
let filePath = path.resolve(__dirname, req.url === '/' ? `index.html` : '1.jpg')
const fileContent = fs.readFileSync(filePath)
const hash = crypto.createHash('md5').update(fileContent).digest('hex')
const etag = `"${hash}"`// etag需要加双引号
const header = {'Content-Type': req.url === '/' ? 'text/html; charset=utf-8' : 'image/png','Etag': etag}
if (req.headers['if-none-match'] === etag) {res.writeHead(304, header)res.end()} else {res.writeHead(200, header)const fileStream = fs.createReadStream(filePath)return fileStream.pipe(res)}
})server.on('clientError', (err, socket) => {socket.end('HTTP/1.1 400 Bad Request\r\n\r\n')
})server.listen(8080, () => {
console.log(`opened server on http://localhost:${server.address().port}`)
})

这里的测试证明就不写,不然这文章的字数就太水了,如果你们有兴趣可以自己尝试一下。

作者:——树深遇鹿

https://juejin.cn/post/7241095368179957820

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

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

相关文章

[CF2057G] Secret Message 题解

神秘题目。 题目的条件十分神奇,\(|A| \le \frac{1}{5} (s+p)\),不知所云。 一开始尝试用皮克定理转化,但是 failed。 阅读理解之后发现有一个(很典)的套路,就是构造出五组方案,使得 \(\sum_{cyc} |A| = s+p\),这样就一定有一组方案,面积小于等于 $ \frac{1}{5} (s+p)…

装机重启后无法进入图形界面

装机重启后无法进入图形界面 A problem has occurred and the system cant recover. Please log out and try again. 主要原因可能是安装的软件包未更新,更新即可 (yum update) 报错截图首先进入命令行界面并登录root账户 Ctrl+Alt+F2联网 对于rocky系统,查看网络设备: nmcl…

【MATLAB】自学记录之基于某楼栋房价数据绘制三维网格图

1. 前言 基于某小区某一楼栋各个户型及楼层之间对应的出售价格表,通过MATLAB脚本进行读取解析,并绘制成三维网格图,从而能够直观地以可视化的角度观察户型位置(东边户、西边户、中间连廊户)、楼层位置(高中低楼层)等因素是否与出售价格存在一定的影响关系。2. 预置条件序…

【前端】谈谈水印实现的几种方式

遇到问题 日常工作中,经常会遇到很多敏感的数据,为防止数据的泄露,我们要在数据上做一些”包装“。目的就是让那些有心泄露数据的”不法分子“迫于严重的”舆论压力“而放弃不法行为,使之”犯罪未遂“,达到不战而屈人之兵的效果。而在安全部门工作的我们,数据安全的观念早…

插头DP记录

关于插头dp。AAA黑题批发。 这个东西好像设问还挺广泛的,做到哪写到哪吧。 得先了解一下轮廓线dp定义。 概念 设问广泛但是总体来说是连通性相关状压dp的一类设计方法。 骨牌覆盖问题 比如说,最简单的,问你 \(n*m\) 的棋盘格里能放多少 \(1*2\) 的骨牌。 考虑把一个节点分为…

03_LaTeX之文档元素

在知道了如何输入文字后,在本章了解一个结构化的文档所依赖的各种元素——章节、目录、列表、图表、交叉引用、脚注等等。目录03_\(\LaTeX{}\) 之文档元素章节和目录章节标题目录文档结构的划分标题页交叉引用脚注和边注特殊环境列表对齐环境引用环境摘要环境代码环境表格列格…

THREE.js学习笔记6——Geometries

这一小节学习THREE.js中的物理模型。 什么是geometry?(英文解释,翻译为中文就看不懂了,直接看英语吧)Composed of vertices (point coordinates in 3D spaces)and faces (triangles that join those vertices to create a surface) Can be used for meshes but also for par…

第三节 回归实战

数据处理超参:人为指定不能改变测试数据只有x没有标签y 训练数据拆分,82开,作训练集和验证集(验证模型好坏),模型训练不是一路上升的过程,训练几次验证一次,最好的模型save下来 one-hot独热编码 猪(1 0 0) 狗(0 1 0) 猫(0 0 1) def get_feature_importance(feature_data, label…

Windows git bash 文字显示/斜杠开头数字

前言全局说明Windows git bash 文字显示/斜杠开头数字一、说明 详细介绍:https://zhuanlan.zhihu.com/p/133706032二、问题三、解决方法 git config --global core.quotepath false免责声明:本号所涉及内容仅供安全研究与教学使用,如出现其他风险,后果自负。参考、来源: h…

DDR 带宽的计算与监控

DDR 带宽(Double Data Rate Bandwidth)是指 DDR 内存在一秒内可以传输的数据量,通常以 GB/s(Gigabytes per second) 为单位。它是衡量内存系统性能的重要指标,直接影响系统的数据吞吐能力。 1.如何计算 DDR 带宽 计算 DDR 理论带宽的公式为: DDR主频 * 位宽 = 理论带宽其…

1.15

尽力了,之前的粗心导致现在要改很多以前的坑,明天再继续

中考英语优秀范文-热点话题-传统文化-006 Welcome to Chinese Summer Camp 欢迎参加中国夏令营

1 写作要求 假定你是李华,你校今年暑假将为外国学生举办一场汉语夏令营活动(Chinese Summer Camp)。请你根据下面海报的内容,用英语给你的笔友David写一封电子邮件,介绍本次活动并邀请他参加。词数80左右。 Welcome to Chinese Summer Camp Time:July 18th—July 28th, 2…