1.为什么浏览器会有缓存?
使用浏览器缓存
之后,除第一次
访问需要向服务器请求网站全部资源,后续访问可以重复使用浏览器缓存
,减少对服务器的请求,降低
服务器的压力。
2.资源请求流程图
3.代码验证(强缓存)
- http1.0 强制缓存
Expires
const Koa = require('koa')
const Router = require('@koa/router')
const fs = require('fs')
const app = new Koa()
const router = new Router()
router.get('/style.css', (ctx) => {
console.log('请求了style.css');
ctx.set('Expires', new Date(Date.now() + 10 * 1000).toUTCString() ) // 缓存10秒
ctx.body = `
body {
background: green;
}
`
})
router.get('/index.html', (ctx) => {
// ****当前访问页不会被强缓存。只有在当前访问页内请求资源,这些资源才可设置缓存。****
ctx.body = `
<html>
<title>浏览器http1.0强制缓存</title>
<link rel="stylesheet" href="./style.css"></link>
</html>
`;
})
app.use(router.routes()).use(router.allowedMethods());
app.listen(4399, () => {
console.log('service run success');
})
打开浏览器NetWork
观察
第一次请求style.css
,响应头携带Expires
缓存时间返回给客户端。
再次请求style.css
,浏览器会比对客户端时间和缓存时间,如果客户端时间在缓存时间内
,则说明命中缓存。直接返回缓存资源。(这里的时间对比可能有问题,服务器时间和客户端时间不同步的问题)
过10s后再次请求style.css
,缓存失效,请求服务器。
服务端打印
- http1.1 强制缓存
Cache-Control
MDN
const Koa = require('koa')
const Router = require('@koa/router')
const fs = require('fs')
const app = new Koa()
const router = new Router()
router.get('/style.css', (ctx) => {
console.log('请求了style.css');
// 缓存资源在10秒后过期
ctx.set('Cache-Control', 'max-age=10')
ctx.body = `
body {
background: green;
}
`
})
router.get('/index.html', (ctx) => {
// ****当前访问页不会被强缓存。只有在当前访问页内请求资源,这些资源才可设置缓存。****
ctx.body = `
<html>
<title>浏览器http1.1强制缓存</title>
<link rel="stylesheet" href="./style.css"></link>
</html>
`;
})
app.use(router.routes()).use(router.allowedMethods());
app.listen(4399, () => {
console.log('service run success');
})
打开浏览器NetWork
观察
第一次请求style.css
,响应头携带Cache-Control
属性返回
再次请求style.css
,浏览器会比对客户端和缓存的相对时间,在缓存相对时间内则说明命中缓存。直接返回缓存资源。
过10s后再次请求style.css
,缓存失效,请求服务器。
服务端打印
强制缓存总结
- http1.0 服务端设置响应头
Expries
字段,来设置绝对过期时间。服务器时间和客户端时间不一致问题,导致缓存失效。 - http1.1 服务端设置响应头
Cache-Control
字段,来设置相对过期时间。都在客户端这边做时间比对,解决了http1.0Expires
时间不一致的问题。 Cache-Control
和Expries
可以一起在响应头返回。Cache-Control
比Expries
优先级高。
4.代码验证(协商缓存)
- http1.0 强制缓存
Last-Modified/if-modified-sence
const Koa = require('koa')
const Router = require('@koa/router')
const fs = require('fs')
const app = new Koa()
const router = new Router()
// http1.0 通过资源修改时间来判断是否采用缓存(只能精确到秒) 是不精准的。
// 资源文件在1秒内修改多次,缓存会失效。
router.get('/style.css', (ctx) => {
ctx.set('Cache-Control', 'no-cache')
const ifModifiedSince = ctx.get('if-modified-since')
const stat = fs.statSync('./style.css')
const ctime = stat.ctime.toUTCString()
if(ctime === ifModifiedSince) {
ctx.status = 304
return
}
ctx.set('Last-Modified', ctime)
ctx.body = fs.createReadStream('./style.css')
})
router.get('/index.html', (ctx) => {
// ****当前访问页不会被强缓存。只有在当前访问页内请求资源,这些资源才可设置缓存。****
ctx.body = `
<html>
<title>浏览器http1.0强制缓存</title>
<link rel="stylesheet" href="./style.css"></link>
</html>
`;
})
app.use(router.routes()).use(router.allowedMethods());
app.listen(4399, () => {
console.log('service run success');
})
打开浏览器NetWork
观察
第一次请求style.css
,响应头携带Last-Modified
缓存时间返回给客户端。
再次请求style.css
,客户端会把上次Last-Modified
时间放在请求头if-modified-sence
上。
服务端接收到客户端请求后,对比请求头携带的文件修改时间ifModifiedSince
和当前文件最后修改时间ctime
做对比。如果相同就返回304状态码,响应体返回空。
const ifModifiedSince = ctx.get('if-modified-since')
const stat = fs.statSync('./style.css')
const ctime = stat.ctime.toUTCString()
if(ctime === ifModifiedSince) {
ctx.status = 304
return
}
- http1.1 强制缓存
Etag/if-none-match
const Koa = require('koa')
const Router = require('@koa/router')
const fs = require('fs')
const app = new Koa()
const router = new Router()
const crypto = require('crypto')
const md5 = (content) => crypto.createHash('md5').update(content).digest('base64')
// http1.1 md5
// 缺点 如果把资源都摘要一遍 性能不太好。
// 解决 一般不会摘要其全部类容,只摘要部分类容 + 文件大小
router.get('/style.css', (ctx) => {
const ifNoneMatch = ctx.get('if-none-match')
const fileContent = fs.readFileSync('./style.css')
const etag = md5(fileContent)
if(ifNoneMatch === etag) {
ctx.status = 304
return
}
ctx.set('Etag', etag)
ctx.body = fileContent
})
router.get('/index.html', (ctx) => {
// ****当前访问页不会被强缓存。只有在当前访问页内请求资源,这些资源才可设置缓存。****
ctx.body = `
<html>
<title>浏览器http1.1强制缓存</title>
<link rel="stylesheet" href="./style.css"></link>
</html>
`;
})
app.use(router.routes()).use(router.allowedMethods());
app.listen(4399, () => {
console.log('service run success');
})
打开浏览器NetWork
观察
第一次请求style.css
,响应头携带Etag``md5
摘要返回给客户端。
再次请求style.css
,客户端会把上次Etag
摘要放在请求头if-no-match
上。
服务端接收到客户端请求后,对比请求头携带的md5
摘要ifNoneMatch
和当前文件摘要etag
做对比。如果相同就返回304状态码,响应体返回空。
const ifNoneMatch = ctx.get('if-none-match')
const fileContent = fs.readFileSync('./style.css')
const etag = md5(fileContent)
if(ifNoneMatch === etag) {
ctx.status = 304
return
}
协商缓存总结
- http1.0 基于
Last-Modified
和if-modified-sence
来对比文件最后修改时间。如果两者相同,就返回304
状态码,响应体返回空。缺点:文件修改时间单位是秒,如果在1秒内修改多次,缓存就会失效。 - http1.1 基于
Etag
和if-none-match
来对比md5
摘要,如果两者相同,就返回304
状态码,响应体返回空。缺点:如果摘要全部资源,性能不太好。一般不会摘要全部,只摘要部分内容 + 文件大小。
原文链接:https://juejin.cn/post/7214318587431141434 作者:ty66