浏览器强缓存与协商缓存详解以及实践

news2024/11/19 10:19:00

"我这边把代码更新上服务器了,你那边看一下呢?"
"我这边还有这个问题,你改没改哦?"
"我改了啊,不信你看我代码......噢,可能是浏览器缓存问题, 你F5刷新一下试试,如果不行 就Ctrl + F5强刷一次试下"
"我强刷了一次,这下可以了"
以上对话大家可能会比较熟悉,可能被浏览器的缓存问题困扰了不久,让我们今天就来揭开浏览器缓存的神秘面纱。

一、到底什么是浏览器缓存?

浏览器缓存(Browser Caching)是为了节约网络的资源加速浏览,浏览器在用户磁盘上对最近请求过的文档进行存储,当访问者再次请求这个页面时,浏览器就可以从本地磁盘显示文档,这样就可以加速页面的阅览。

以上是百度百科对于浏览器缓存的定义
可见浏览器缓存目的:为了节约网络的资源加速浏览
方式是:对请求的文档进行存储
文档具体就是指:html , js , css , 图片,其他媒体文件等

二、浏览器缓存的规则?

如果对所有请求的资源都进行缓存,那肯定是不行的。所以肯定是需要一套规则来指定什么资源应该存在浏览器中存多久。而这套规则就是http协议。

浏览器根据该请求返回的响应头(respone header)的内容来决定该资源是否会被缓存,会存多久。

浏览器缓存可以分为两类:

1.彻底缓存(强缓存)

  • 定义:彻底缓存就是浏览器直接读取缓存,不发出任何请求,性能提升最大
  • 会让浏览器执行强缓存的respone header有以下两种:
    1.Cache-Control:max-age=3600;
    2.Expires: Thu, 25 May 2020 12:30:00 GMT;

我们分别来看一下这两个http header

Cache-Control

浏览器缓存里, Cache-Control是金字塔顶尖的规则, 它藐视一切其他设置, 只要其他设置与其抵触, 一律覆盖之.不仅如此, 它还是一个复合规则, 包含多种值, 横跨 存储策略, 过期策略 两种, 同时在请求头和响应头都可设置.

语法为: “Cache-Control : cache-directive”.


上文的max-age=3600即代表该资源会在浏览器缓存3600秒,即1个小时。在获取到该资源后的1小时内,若浏览器再一次请求该资源,将不会发出网络请求,直接读浏览器的缓存中读取。

假设所请求资源于4月5日缓存, 且在4月12日过期.
当max-age 与 max-stale 和 min-fresh 同时使用时, 它们的设置相互之间独立生效, 最为保守的缓存策略总是有效. 这意味着, 如果max-age=10 days, max-stale=2 days, min-fresh=3 days, 那么:

  • 根据max-age的设置, 覆盖原缓存周期, 缓存资源将在4月15日失效(5+10=15);
  • 根据max-stale的设置, 缓存过期后两天依然有效, 此时响应将返回110(Response is stale)状态码, 缓存资源将在4月14日失效(12+2=14);
  • 根据min-fresh的设置, 至少要留有3天的新鲜期, 缓存资源将在4月9日失效(12-3=9);

由于客户端总是采用最保守的缓存策略, 因此, 4月9日后, 对于该资源的请求将重新向服务器发起验证.

Expires

这是 Http/1.0 规定的响应头,它的含义就是代表该资源在未来某个时间点失效,比如:

Expires: Thu, 25 May 2020 12:30:00 GMT

代表该资源在2020年5月25日12点30分(格林威治时间)失效。
Expires有一个很大的弊端,就是它返回的是服务器的时间,但判断的时候用的却是客户端的时间,这就导致Expires很被动,因为用户有可能改变客户端的时间,导致缓存时间判断出错,缓存可能失效。
上面的例子中,我将客户端的时间改成Thu, 25 May 2020 12:31:00 GMT,浏览器 这也是引入Cache-Control:max-age指令的原因之一。
因为Cache-Control:max-age没有依靠客户端的时间,就算客户端时间改变到了本应过期的时间,而实际时间并没有达到过期时间,缓存不会失效。

2.协商缓存

  • 定义:协商缓存就是浏览器向服务器发送一个请求,服务器会检查该资源是否有更新,如果有更新,就返回最新的资源,状态码200,如果没有更新,状态码304,不返回资源,浏览器从缓存中读取资源。

协商缓存字段之ETag

服务器可以通过某种自定的算法对资源生成一个唯一的标识(比如md5标识),然后在浏览器第一次请求某一个URL时把这个标识放到响应头传到浏览器,浏览器会把这个ETag的值存起来,服务器端的返回状态会是200。
就像下面这张图一样。



以后如果浏览器要再发送该请求,会在request header 中加上If-None-Match(具体格式看下图), 而该请求头的值就是上一次存的ETag的值,用以发送给服务端来验证资源有没有修改。

Get请求中,当且仅当服务器上没有任何资源的ETag属性值与这个首部中列出的相匹配的时候,服务器端会才返回所请求的资源,响应码为200。

如果有资源的ETag值相匹配,那么返回304状态码。浏览器就会从缓存中获取该请求资源,从而达到节省开销加快用户访问速度的目的

协商缓存字段之Last-Modified

当Response Header中没有ETag,Cache-Control,Expires,Pragma这类缓存相关字段,只有Last-Modified,浏览器也会缓存,理论上,应该会在下一次请求中带上If-Modified-Since的请求头,去服务端验证资源是否过期,过期就响应码就为200并返回相应的资源,没过期响应码就是304,浏览器会从缓存中获取资源。
但实际上,各个浏览器对这部分的实现不太相同。
Chrome浏览器对此的缓存机制是,浏览器会有一套算法去判断当前资源是否过期。
如果没有过期的话,直接从缓存中取资源,不会发送请求。
用fiddler可以看到第一次请求了很多JS和css文件,但第二次请求 却没有再请求相关资源了,说明直接从缓存读取。


所以chrome浏览器有自己一套算法(下文会提到该算法)来判断该资源是否过期,如果没过期就直接读取缓存,而过期了才会去服务端验证该资源是否过期。


而FireFox不一样,如果是在上面的情况下缓存了该资源,会直接请求服务器来验证资源是否过期,如下图。


在测试中,我发现在状态码304的情况下,该测试服务器返回了 一个Etag字段,但下次请求,浏览器并未在请求头中加上 If-None-Match字段, 我猜测304状态码下的Etag, 不会触发该字段的缓存机制。


 

大多数浏览器都是像Chrome一样的做法。

Chrome浏览器自己对于Last-Modified的缓存计算如下:

如下资源便采取了启发式缓存算法.

根据响应头中2个时间字段 Date 和 Last-Modified 之间的时间差值,取其值的10%作为缓存时间周期。计算如下:

const Date_value = new Date('Thu, 06 Apr 2017 01:30:56 GMT').getTime();
const LastModified_value = new Date('Thu, 01 Dec 2016 06:23:23 GMT').getTime();
const cacheTime = (Date_value - LastModified_value) / 10;
const Expires_timestamp = Date_value + cacheTime;
const Expires_value = new Date(Expires_timestamp);
console.log('Expires:', Expires_value); // Expires: Tue Apr 18 2017 23:25:41 GMT+0800 (CST)

可见该资源将于2017年4月18日23点25分41秒过期, 尝试以下两步进行验证:

  1. 试着把本地时间修改为2017年4月18日23点25分40秒, 迅速刷新页面, 发现强缓存依然有效(依旧是200 OK (from disk cache)).


2) 然后又修改本地时间为2017年4月18日23点26分40秒(即往后拨1分钟), 刷新页面, 发现缓存已过期, 此时浏览器重新向服务器发起了验证, 且命中了304协商缓存, 如下所示.

可见, 启发式缓存算法采用的缓存时间可长可短, 因此对于常规资源, 建议明确设置缓存时间(如指定max-age 或 expires)

缓存字段之Pragma

http1.0字段, 通常设置为Pragma:no-cache, 作用同Cache-Control:no-cache.该字段通常作为兼容字段出现, 当一个no-cache请求发送给一个不遵循HTTP/1.1的服务器时, 客户端应该包含pragma指令. 为此, 勾选☑️ 上disable cache时或者Ctrl+F5强制刷新时, 浏览器自动带上了pragma字段和Cache-control:no-cache. 如下:

该如何设置缓存才能达到性能最优?

不同的网站应该有不同的缓存策略,应该结合网站的业务来制定适合的缓存策略。
下面简单聊几种情况

对于基于webpack打包构建生成的spa项目

  1. index.html 不做缓存,每次请求都获取最新版本
  1. 使用 webpack 等 build 后(以文件内容为hash生成文件后缀)的其他所有资源文件(包括 js、css 和图片等),都做强缓存(一个月打底,可以设置一年)
    详情可以查看这篇文章

很久都不会发生变化的静态文件,可以设置长时间的强缓存

比如说jquery的cdn就可以看到设置的强缓存100年- -`

cache-control: max-age=315360000

对于经常发生变化的静态资源,可以设置etag来使用协商缓存

参考:
浏览器缓存机制剖析
Caching best practices & max-age gotchas
best-practices-for-cache-control-settings-for-your-website

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

超星章节内ppt课件下载

超星章节内课件下载 欢迎访问个人博客:www.xuanworld.top 前言 ​ 一般来说,如果老师设置超星学习通章节内的ppt加密,那么ppt是无法下载的,超星不会提供下载接口,但是我们可以通过网络抓包的方式来截取到pdf&#x…

vue-router控制台异常:Uncaught (in promise) Error: Redirected when going from “/“ to “/foo“

qian kun微前端在子应用的路由配置中添加了一个全局前置导航守beforeEach,在前置导航守卫中调用next方法时重写了路由的path,结果控制台每次在路由跳转时都会报异常,但是不影响功能。 这里,我们将这个场景从微前端摘出来&#xf…

uniapp登录拦截器(未登录点击其他地方跳转登录页)

项目场景: 例如:客户要求用户在未登录的情况下,用户只可以在底部导航栏操作,点击其他的的功能都要跳转到登录页面。 uni.addInterceptor拦截器 是uni-app官网提供的拦截器,可以利用它来实现登录拦截器,用户…

vue3+pinia+vuerouter4动态路由菜单

文章目录前言一、用户权限和菜单列表数据二、pinia存储数据状态共享1.创建存储用户详情的user.ts文件2.创建存储用户菜单和权限的menus.ts文件三、设置动态路由1.在router文件夹下面创建routers.ts文件2.设置前置路由守卫3.左侧导航菜单前言 最近在做一个通用后台管理系统的框架…

35.JavaScript对象和数组的解构赋值基础详解、let陷阱、函数参数解构

文章目录35.JavaScript对象和数组的解构赋值数组解构解构不改变原数组忽略数组元素可迭代对象使用解构赋值给任何变量与.entries()方法结合与Map结合变量交换多余的元素对象解构属性变量映射默认值多余的属性let陷阱多层解析函数参数解析总结35.JavaScript对象和数组的解构赋值…

HTML使用Element-UI制作管理系统页面(无需脚手架以及创建vue工程)

HTML正常使用Element-UI前言尝试经历设计附件前言 入职培训到Web前端后布置了一个制作管理系统前端页面的任务,任务要求包含: 1.页面的布局主要为左侧导航菜单栏,右侧为信息展示栏,要体现嵌套 2.点击菜单栏切换右侧信息展示时左侧…

前端在项目中使用mockjs模拟数据的增删改查

背景 在项目开发时,会存在前端界面已经画好了但是后端接口还在开发的情况,此时前端可以先根据接口文档明确自己需要的字段,然后使用mock模拟后端接口进行调试 安装 npm install mockjs 使用 1. 创建vue项目之后,新建一个mock…

基于SpringBoot的校园疫情防控系统设计与实现

1.概述 校园疫情防控系统的开发运用java技术、springboot框架,MIS的总体思想,以及Mysql等技术的支持下共同完成了该系统的开发,实现了校园疫情防控管理的信息化,使用户体验到校园疫情防控管理,管理员管理操作将更加方…

使用 el-table 实现树形数据懒加载、点击行展开、每次只展示一条数据(大类)以及自定义表格合计值

1. 使用 el-table 实现树形数据懒加载 实现必需条件: lazy :load“loadNode” :tree-props“{ children: ‘children’, hasChildren: ‘hasChildren’ }” 注意:特别是第3条,后端接口必须传给你"hasChildren"(名字可以不一样)…

Vue3的vue-router超详细使用

从零开始搭建Vue3环境(vitetsvue-router),手拉手做一个router项目搭建vue3环境vue-router入门(宝宝模式)vue-router基础(青年模式)一。动态路由匹配1.带参数的动态路由匹配2.捕获所有路由或404 …

uniapp项目中引入vant-Weapp(局部全局都有 史上最详细的方法)

1.先在根目录创建wxcomponents文件夹 2.打开 https://github.com/youzan/vant-weapp 下载最新的vant-Weapp 3.把我们下好的文件vant-weapp里面只留下dist其余的可以全部删掉,然后把vant-weapp放到 wxcomponents里面 4.在App.vue引入vant样式 import /wxcomponents…

蓝桥杯web开发-5道模拟题让你信心满满

📋 个人简介 💖 作者简介:大家好,我是阿牛,全栈领域新星创作者。😜📝 个人主页:馆主阿牛🔥🎉 支持我:点赞👍收藏⭐️留言&#x1f4d…

最好的Vue组件库之Vuetify的入坑指南(持续更新中)

目录 安装Vuetify 文档结构 快速入门 特性 样式和动画 首先先声明,个人不是什么很牛逼的大佬,只是想向那些想入坑Vuetify的前端新手或者嫌文档太长不知如何入手的人提供一些浅显的建议而已,能让你们稍微少走一些弯路就是本文的目的。我其实也…

『从零开始学小程序』媒体组件video组件

👨‍🎓作者简介:一位喜欢写作,计科专业大三菜鸟 🏡个人主页:starry陆离 🕒首发日期:2022年9月15日星期四 如果文章有帮到你的话记得点赞👍收藏💗支持一下哦 『…

Vue结合高德地图实现HTML写自定义信息弹窗

最近在写项目的时候有个需求就是根据点击地图上的点展示对应的信息,弹窗看着还挺花哨的。我在高德地图官网上还有各大平台找了如何自定义弹窗,可给出的大多数都是通过JS写HTML结构,我感觉这种不仅不好布局,而且可读性和维护性都不…

客户端会话跟踪技术 Cookie 浅谈

文章目录前言为什么之前浏览器和服务器不支持数据共享?会话跟踪技术Cookie的概念Cookie的工作流程Cookie的基本使用Cookie原理分析Cookie的存活时间Cookie存储中文前言 用户打开浏览器,第一次访问 Web 服务器资源时,会话建立,直到…

富文本编辑器Quill 介绍及在Vue中的使用方法

在Web开发中,富文本编辑器是不可或缺的一个功能组件,掌握少量基础语法就能让一篇文章实现较为不错的排版效果,即见即所得。 目前市场上已提供大量集成富文本编辑器的包,Quill 作为其中一个,以简单、易上手特点&#x…

vue项目打包失败问题记录

项目"vue": "^2.7.14"版本 起因:项目里安装了openlayers最新版本的地图插件,打包会成功,但是打包页面会有红色提示 刚开始根据红色提示百度找到相同错误的方法提供了的一系列提示安装啊,卸载,装了…

【WebSocket 协议】Web 通信的下一步进化

标题【手动狗头🐶】,大佬轻饶 目录一、什么是 WebSocket ?二、WebSocket 应用场景?三、代码中的 WebSocket四、一个完美的案例:在线聊天程序实现服务器chat/index.js实现客户端chat/index.htmlchat/style.css最终效果WebSocket 是基于单个 …

关于elementUI表单的清除验证以及复合型输入框

目录 一、清除表单的验证 问题的发生以及解决过程 代码 总结 二、复合型输入框——查询(前置和后置都有的) 问题的发生以及解决过程 代码 展示 一、清除表单的验证 问题的发生以及解决过程 表单弹窗关闭后再打开会出现上一次的验证信息提示&am…