前言
大家好,我是林三心,用最通俗易懂的话讲最难的知识点是我的座右铭,基础是进阶的前提是我的初心
背景
无论是开发中或者是面试中,HTTP缓存都是非常重要的,这体现在了两个方面:
HTTP缓存 可以提高前端页面的性能 HTTP缓存 是面试中的高频问点 Nodejs 的简单实践,给大家讲最通俗易懂的HTTP缓存,大家通过这篇文章一定能了解掌握它!!! cache-study ,并准备环境npm init Koa、nodemon npm i koa -D
npm i nodemon -g index.js、index.html、static文件夹 index.html
Document
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缓存 常见的有两类: 强缓存 :可以由这两个字段其中一个决定 expires cache-control(优先级更高) 协商缓存 :可以由这两对字段中的一对决定 Last-Modified,If-Modified-Since Etag,If-None-Match(优先级更高) 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 的字段
memory ,这意味着,通过 expires 设置强缓存的时效是30s,这30s之内,资源都会走本地缓存,而不会重新请求
注意点:有时候你Nodejs代码更新了时效时间,但是发现前端页面还是在走上一次代码的时效,这个时候,你可以把这个 Disabled cache 打钩,然后刷新一下,再取消打钩
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 的值,传到服务端,服务端拿到这个时间跟所请求的资源的最后修改时间进行比对 命中缓存 ,那就返回 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)
}
}
})
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 跟 Last-Modified,If-Modified-Since 大体一样,区别在于: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 的一样,这里就不重复说明了。。。