当前位置:网站首页> 实践示例理解js强缓存协商缓存
实践示例理解js强缓存协商缓存
2022-07-04 18:39:00 【1024问】
背景
前置准备
准备
启动页面
HTTP缓存种类
强缓存
expires
cache-control
协商缓存
Last-Modified,If-Modified-Since
Etag,If-None-Match
总结
背景无论是开发中或者是面试中,HTTP缓存都是非常重要的,这体现在了两个方面:
开发中:合理利用HTTP缓存可以提高前端页面的性能
面试中:HTTP缓存是面试中的高频问点
所以本篇文章,我不讲废话,我就通过Nodejs的简单实践,给大家讲最通俗易懂的HTTP缓存,大家通过这篇文章一定能了解掌握它!!!
前置准备准备创建文件夹cache-study,并准备环境
npm init
安装Koa、nodemon
npm i koa -Dnpm i nodemon -g
创建index.js、index.html、static文件夹
index.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>Document</title> <link rel="stylesheet" href="./static/css/index.css" rel="external nofollow" ></head><body> <div class="box"> </div></body></html>
static/css/index.css
.box { width: 500px; height: 300px; background-image: url('../image/guang.jpg'); background-size: 100% 100%; color: #000;}
static/image/guang.jpg
index.js
const Koa = require('koa')const fs = require('fs')const path = require('path')const mimes = { css: 'text/css', less: 'text/css', gif: 'image/gif', html: 'text/html', ico: 'image/x-icon', jpeg: 'image/jpeg', jpg: 'image/jpeg', js: 'text/javascript', json: 'application/json', pdf: 'application/pdf', png: 'image/png', svg: 'image/svg+xml', swf: 'application/x-shockwave-flash', tiff: 'image/tiff', txt: 'text/plain', wav: 'audio/x-wav', wma: 'audio/x-ms-wma', wmv: 'video/x-ms-wmv', xml: 'text/xml',}// 获取文件的类型function parseMime(url) { // path.extname获取路径中文件的后缀名 let extName = path.extname(url) extName = extName ? extName.slice(1) : 'unknown' return mimes[extName]}// 将文件转成传输所需格式const parseStatic = (dir) => { return new Promise((resolve) => { resolve(fs.readFileSync(dir), 'binary') })}const app = new Koa()app.use(async (ctx) => { const url = ctx.request.url if (url === '/') { // 访问根路径返回index.html ctx.set('Content-Type', 'text/html') ctx.body = await parseStatic('./index.html') } else { const filePath = path.resolve(__dirname, `.${url}`) // 设置类型 ctx.set('Content-Type', parseMime(url)) // 设置传输 ctx.body = await parseStatic(filePath) }})app.listen(9898, () => { console.log('start at port 9898')})
启动页面现在你可以在终端中输入nodemon index,看到下方的显示,则代表成功启动了服务
此时你可以在浏览器链接里输入http://localhost:9898/,打开看到如下页面,则代表页面访问成功!!!
HTTP缓存种类HTTP缓存常见的有两类:
强缓存:可以由这两个字段其中一个决定
expires
cache-control(优先级更高)
协商缓存:可以由这两对字段中的一对决定
强缓存Last-Modified,If-Modified-Since
Etag,If-None-Match(优先级更高)
接下来我们就开始讲强缓存
expires我们只需设置响应头里expires的时间为当前时间 + 30s就行了
app.use(async (ctx) => { const url = ctx.request.url if (url === '/') { // 访问根路径返回index.html ctx.set('Content-Type', 'text/html') ctx.body = await parseStatic('./index.html') } else { const filePath = path.resolve(__dirname, `.${url}`) // 设置类型 ctx.set('Content-Type', parseMime(url)) // 设置 Expires 响应头 const time = new Date(Date.now() + 30000).toUTCString() ctx.set('Expires', time) // 设置传输 ctx.body = await parseStatic(filePath) }})
然后我们在前端页面刷新,我们可以看到请求的资源的响应头里多了一个expires的字段
并且,在30s内,我们刷新之后,看到请求都是走memory,这意味着,通过expires设置强缓存的时效是30s,这30s之内,资源都会走本地缓存,而不会重新请求
注意点:有时候你Nodejs代码更新了时效时间,但是发现前端页面还是在走上一次代码的时效,这个时候,你可以把这个Disabled cache打钩,然后刷新一下,再取消打钩
cache-control其实cache-control跟expires效果差不多,只不过这两个字段设置的值不一样而已,前者设置的是秒数,后者设置的是毫秒数
app.use(async (ctx) => { const url = ctx.request.url if (url === '/') { // 访问根路径返回index.html ctx.set('Content-Type', 'text/html') ctx.body = await parseStatic('./index.html') } else { const filePath = path.resolve(__dirname, `.${url}`) // 设置类型 ctx.set('Content-Type', parseMime(url)) // 设置 Cache-Control 响应头 ctx.set('Cache-Control', 'max-age=30') // 设置传输 ctx.body = await parseStatic(filePath) }})
前端页面响应头多了cache-control这个字段,且30s内都走本地缓存,不会去请求服务端
协商缓存与强缓存不同的是,强缓存是在时效时间内,不走服务端,只走本地缓存;而协商缓存是要走服务端的,如果请求某个资源,去请求服务端时,发现命中缓存则返回304,否则则返回所请求的资源,那怎么才算命中缓存呢?接下来讲讲
Last-Modified,If-Modified-Since简单来说就是:
第一次请求资源时,服务端会把所请求的资源的最后一次修改时间当成响应头中Last-Modified的值发到浏览器并在浏览器存起来
第二次请求资源时,浏览器会把刚刚存储的时间当成请求头中If-Modified-Since的值,传到服务端,服务端拿到这个时间跟所请求的资源的最后修改时间进行比对
比对结果如果两个时间相同,则说明此资源没修改过,那就是命中缓存,那就返回304,如果不相同,则说明此资源修改过了,则没命中缓存,则返回修改过后的新资源
// 获取文件信息const getFileStat = (path) => { return new Promise((resolve) => { fs.stat(path, (_, stat) => { resolve(stat) }) })}app.use(async (ctx) => { const url = ctx.request.url if (url === '/') { // 访问根路径返回index.html ctx.set('Content-Type', 'text/html') ctx.body = await parseStatic('./index.html') } else { const filePath = path.resolve(__dirname, `.${url}`) const ifModifiedSince = ctx.request.header['if-modified-since'] const fileStat = await getFileStat(filePath) console.log(new Date(fileStat.mtime).getTime()) ctx.set('Cache-Control', 'no-cache') ctx.set('Content-Type', parseMime(url)) // 比对时间,mtime为文件最后修改时间 if (ifModifiedSince === fileStat.mtime.toGMTString()) { ctx.status = 304 } else { ctx.set('Last-Modified', fileStat.mtime.toGMTString()) ctx.body = await parseStatic(filePath) } }})
第一次请求时,响应头中:
第二次请求时,请求头中:
由于资源并没修改,则命中缓存,返回304:
此时我们修改一下index.css
.box { width: 500px; height: 300px; background-image: url('../image/guang.jpg'); background-size: 100% 100%; /* 修改这里 */ color: #333;}
然后我们刷新一下页面,index.css变了,所以会没命中缓存,返回200和新资源,而guang.jpg并没有修改,则命中缓存返回304:
Etag,If-None-Match其实Etag,If-None-Match跟Last-Modified,If-Modified-Since大体一样,区别在于:
后者是对比资源最后一次修改时间,来确定资源是否修改了
前者是对比资源内容,来确定资源是否修改
那我们要怎么比对资源内容呢?我们只需要读取资源内容,转成hash值,前后进行比对就行了!!
const crypto = require('crypto')app.use(async (ctx) => { const url = ctx.request.url if (url === '/') { // 访问根路径返回index.html ctx.set('Content-Type', 'text/html') ctx.body = await parseStatic('./index.html') } else { const filePath = path.resolve(__dirname, `.${url}`) const fileBuffer = await parseStatic(filePath) const ifNoneMatch = ctx.request.header['if-none-match'] // 生产内容hash值 const hash = crypto.createHash('md5') hash.update(fileBuffer) const etag = `"${hash.digest('hex')}"` ctx.set('Cache-Control', 'no-cache') ctx.set('Content-Type', parseMime(url)) // 对比hash值 if (ifNoneMatch === etag) { ctx.status = 304 } else { ctx.set('etag', etag) ctx.body = fileBuffer } }})
验证方式跟刚刚Last-Modified,If-Modified-Since的一样,这里就不重复说明了。。。
总结参考 https://www.jb51.net/article/254078.htm
以上就是实践示例理解js强缓存协商缓存的详细内容,更多关于js强缓存协商缓存的资料请关注软件开发网其它相关文章!
边栏推荐
- Swagger突然发癫
- 1002. A+b for Polynomials (25) (PAT class a)
- Cbcgpprogressdlg progress bar used by BCG
- Dark horse programmer - software testing - stage 08 2-linux and database-23-30-process port related, modify file permissions, obtain port number information, program and process related operations, Li
- 需求开发思考
- BCG 使用之CBCGPProgressDlg进度条使用
- HMM hidden Markov model and code implementation
- 【毕业季】绿蚁新醅酒,红泥小火炉。晚来天欲雪,能饮一杯无?
- [problem] Druid reports exception SQL injection violation, part always true condition not allow solution
- kotlin 条件控制
猜你喜欢
C# 使用StopWatch测量程序运行时间
Decryption function calculates "task state and lifecycle management" of asynchronous task capability
Cbcgptabwnd control used by BCG (equivalent to MFC TabControl)
c# .net mvc 使用百度Ueditor富文本框上传文件(图片,视频等)
Free soldier
C语言-入门-基础-语法-流程控制(七)
西门子HMI下载时提示缺少面板映像解决方案
C language - Introduction - Foundation - grammar - process control (VII)
BCG 使用之CBCGPProgressDlg进度条使用
TCP waves twice, have you seen it? What about four handshakes?
随机推荐
Development and construction of DFI ecological NFT mobile mining system
YOLOv5s-ShuffleNetV2
[problem] Druid reports exception SQL injection violation, part always true condition not allow solution
HMM hidden Markov model and code implementation
Template_ Judging prime_ Square root / six prime method
C# 使用StopWatch测量程序运行时间
记一次 .NET 某工控数据采集平台 线程数 爆高分析
更强的 JsonPath 兼容性及性能测试之2022版(Snack3,Fastjson2,jayway.jsonpath)
Crystal optoelectronics: ar-hud products of Chang'an dark blue sl03 are supplied by the company
什么叫内卷?
[QNX hypervisor 2.2 user manual]6.3.1 factory page and control page
复杂因子计算优化案例:深度不平衡、买卖压力指标、波动率计算
Socket programming demo II
黑马程序员-软件测试--07阶段2-linux和数据库-09-24-linux命令学习步骤,通配符,绝对路径,相对路径,文件和目录常用命令,文件内容相关操作,查看日志文件,ping命令使用,
Kotlin inheritance
1003 emergency (25 points) (PAT class a)
TCP两次挥手,你见过吗?那四次握手呢?
Find the nth power of 2
kotlin 类和对象
Write it down once Net analysis of thread burst height of an industrial control data acquisition platform