浏览器缓存(http缓存)前言 今天聊一聊浏览器的缓存,但是不是浏览器的存储,这两个概念的区分开。 浏览器的存储例如:c
前言
今天聊一聊浏览器的缓存,但是不是浏览器的存储,这两个概念的区分开。
浏览器的存储例如:cookies、localstorage、sessionstorage、indexedDB(浏览器端的数据库)
先来聊一聊存储上的一些区别:
- cookies:有效时间:后端设置。存储大小:4k左右。自动携带在http请求头中。
- localstorage:有效时间:一直存在,除非手动清理。存储大小:5-8M。不参与后端通讯。
- sessionstorage:有效时间:页面关闭消失.存储大小:5-8M。不参与后端通讯
- indexedDB:有效时间:一直存在。存储大小:理论无限大(计算机自带内存)。不参与后端通讯
关于跨域:
- cookie:登录是基于session的,成功登录,服务器set-cookie将cookie携带在浏览器中,当访问同源api时,cookie会自动带上,但是如果发送跨域请求,浏览器是不会主动带上cookie的,如果要带上cookie则需要人为的在请求头中添加一些字段,例如:credentials:true 字段same-site
- loacalstorage:跨域存储(postMessage)
- sessionstorage:不能跨域
- IndexedDB:不能跨域(默认)
再来着重聊一聊cookies,其中有几个属性可以认为设置
- value:cookie(加密后的用户表示)
- http-only:只允许在http传输的时候携带(例如不允许js访问,可以减少XSS攻击)
- secure:只能https协议携带
- same-site:规定浏览器不能在跨域请求中携带cookie
浏览器的缓存
缓存应该比较容易理解,例如打开一个网站,第一次打开肯定需要将全部资源加载一遍,但是后面再次打开,有很多资源都是重复的,例如一些图标,或者nav等等,这些东西就不需要重新发请求资源了,因为他们加入缓存,这些资源直接从缓存中取就行,不需要再发请求了。Cache storage便是一个缓存区间。
下面我们来搭建一个前后端,便于理解。
搭建一个后端,举个例子,前端访问localhost:3000/index.html时,后端能够把这个html页面返回给前端,但是html页面中存在图片,后端还需要将这个图片返回给前端
const http = require('http');
const path = require('path');
const url = require('url');
const fs = require('fs');
// mime 判断前端请求的路径要的是什么类型
const mime = require('mime')
const server = http.createServer((req, res) => {
// console.log(req.url);
let filePath = path.resolve(__dirname, path.join('www', req.url))
// console.log(filePath);
if (fs.existsSync(filePath)) {
const stat = fs.statSync(filePath)
// console.log(stat);
const {ext} = path.parse(filePath) // 文件后缀
if (stat.isDirectory()) {
filePath = path.join(filePath, 'index.html')
}
if (!stat.isDirectory() && fs.existsSync(filePath)) {
const content = fs.readFileSync(filePath)
console.log(content);
// if (ext === '.png') {
// res.writeHead(200, { 'Content-type': 'image/png' })
// }else{
// res.writeHead(200, { 'Content-type': 'text/html;charset=utf-8' })
// }
res.writeHead(200, { 'Content-type': mime.getType(ext) })
res.end(content)
}
}
})
server.listen(3000, () => {
console.log('server is running');
})
因此,我们在写响应头的时候不可以写死了返回的资源类型是html或者是图片格式,虽然此时如果直接写死了类型为html,在谷歌中还是能得到这张图片,只是因为现在的谷歌过于强大了,换做是老一些的浏览器就解析不出来了,因此我们引入了第三方库mime,判断资源类型然后在写响应头的时候直接通过mime.getType然后传入后缀以变量的形式传递。注意:mime4不支持require的形式引入,因此我们下载mime@3
至此就是给大家介绍了一下前后端之间的协商来处理资源处理的类型,接下来我们要考虑的是,比如这份html就是百度的首页,而这里面的图片是死的,比如是logo,我们并不希望每次访问这个图片都去重新加载一份,我们希望第一次访问,从后端请求到资源,然后在浏览器中做好缓存,下一次就不需要发请求了,直接从浏览器中读出来,这样能够让页面加载的速度更快。
强缓存
'cache-control':'max-age=86400'
res.writeHead(200, {
'Content-type': mime.getType(ext) ,
'cache-control':'max-age=86400' // ms 一天 缓存控制
})
res.end(content)
依旧是通过在响应头上添加一些字段,通知浏览器某些资源需要进行缓存。
可以看见,刷新一次页面以后,在获取这张图片资源时,履行者编程了缓存,耗时0s。
那么这份html资源为什么没被缓存起来呢?响应头里也确实加上了缓存控制。
事实上,由于这份资源的请求方式是我们通过输入url地址,xxxx:3000/index.html进行请求的,这种方式的请求头里一定会有'cache-control':'max-age=no-cache',因此就算后端返回的是控制了时间为一天,也不会缓存下来。
协商缓存
'last-modified':资源最后一次修改的时间
试想:刚才那个场景下,这张图片已经定死了,在规定时间内被缓存起来了,举个例子,百度的首页logo也会被缓存,并且它的缓存时间可能是这台机器的一生,但是每年中秋国庆,这张logo图都会被加上一些特色,又由于你已经进行强缓存,这张图片已经焊死在这,他不会请求新的资源(特色图片)即:资源一旦被缓存,在过期时间到达之前,浏览器是不会向服务器发起资源请求的 ,此时如何处理?
const timeStamp = req.headers['if-modified-since']
let status = 200
if (timeStamp && Number(timeStamp) === stat.mtimeMs) { // 资源没变更
status
}
if (!stat.isDirectory() && fs.existsSync(filePath)) {
const content = fs.readFileSync(filePath)
console.log(content);
// if (ext === '.png') {
// res.writeHead(200, { 'Content-type': 'image/png' })
// }else{
// res.writeHead(200, { 'Content-type': 'text/html;charset=utf-8' })
// }
res.writeHead(status, {
'Content-type': mime.getType(ext),
'cache-control': 'max-age=86400' , // ms 一天 缓存控制
'last-modified':stat.mtimeMs // 资源最新修改时间
})
res.end(content)
}
其实刚刚说的场景不对,节假日加上特色图片,通过协商缓存达不到我们要的效果,资源不会重新更新,而是通过修改文件名,logo.xxx哈希值.png,文件名变了请求的资源也就变了。
- 后端设置响应头,'last-modified':'资源最后一次修改的时间',浏览器就会在下一次请求资源时在请求头中默认携带if-modified-since:'资源最后一次修改的时间',后端通过对比请求头中的时间戳和当前资源最后一
- 次修改的时间,来判断该资源是否有更新,如果没有更新,则向浏览器返回304状态码,驱使浏览器复用上一次的资源
- 后端设置响应头'etag':'由文件内容加密得到的值',如果用上述方法,当一份html我们添加了xx东西然后又撤销了这份操作,事实上这份html被修改了两次,这样这份资源就需要重新请求,因此可以用etag内容生成加密值来判断,内容是否变更。
小结
本文首先讲解了浏览器的存储
问题,然后着重介绍了浏览器缓存
包括强缓存和协商缓存
,以及代码实现。
转载自:https://juejin.cn/post/7412124636240085043