什么是前端缓存

什么是前端缓存大家在日常的开发工作过程中 有没有遇到过下面几种情况 部署 发布前端工程后 增加的功能或修改的 bug 没有生效测试同学测试功能时经常暴力地清除所有浏览器缓存前端开发同学经常说 你 强刷 一下就好了 遇到上面这些情况 大部分同学就知道了 这是前端有缓存的原因 那具体什么是前端缓存呢 前端缓存仅仅和前端有关系吗 前端缓存 浏览器缓存前端缓存 是浏览器为了提升网站的加载性能 缩短用户

大家在日常的开发工作过程中,有没有遇到过下面几种情况:

  • 部署/发布前端工程后,增加的功能或修改的bug没有生效
  • 测试同学测试功能时经常暴力地清除所有浏览器缓存
  • 前端开发同学经常说:你“强刷”一下就好了

遇到上面这些情况,大部分同学就知道了,这是前端有缓存的原因,那具体什么是前端缓存呢,前端缓存仅仅和前端有关系吗?

前端缓存 / 浏览器缓存

前端缓存,是浏览器为了提升网站的加载性能,缩短用户等待时间而采取的措施,浏览器总是想尽量少地向服务器发送请求,能够从自己保存的副本中得到的,就不去麻烦服务器了,毕竟自己动手丰衣足食嘛,所以更准确的叫法应该为浏览器缓存,下文中如果出现缓存等字眼,指的就是前端缓存浏览器缓存

由上所述,缓存对于用户来说是友好的,而且对大多数用户透明的,普通用户可能最多只是感觉再次进入一个网站时速度变快了而已,再进一步可能某些用户发现一些静态页面断网后还能被访问。但是对于开发同学就需要对缓存有所了解,并在发布新版时特别注意。

缓存机制

那缓存是具体是什么呢?我们可以将其理解为我们下载到硬盘的文件,比如说老师为我们制作了一份课件,我们将其下载放在了硬盘中,那之后我们什么时候想看,只需要到这份课件保存的文件夹内,将其打开就好了,而不必再次下载了。但是有一天老师发现了课件里面有一些问题,那老师为了不误导我们肯定想要修正这些问题,修正过后呢,老师在上课的时候就告诉我们“同学们,你们上次下载的课件有些问题,我已经改了,你们再重新下载一下,之前那份就不要看了”。那我们回去之后就重新下载老师修改过后的课件,之前的课件就再也不需要看了。

明白了上面的例子,其实也就明白了缓存的机制,对应于缓存,工作流程应该是这样的:

用户小U使用浏览器小B访问了一个页面P,浏览器将页面P和其中包含的资源文件(一般包含css、js、图片等文件)保存下来了,一段时间内呢小B反复访问页面P,小B都会从自己的存储区取出相应文件为小U渲染出同样的页面P。然后有一天页面P更新了,比如说主题变了或者里面的按钮功能变化了,当小B再次访问页面P时,它知道自己之前保存的页面P的资源文件已经过时了,然后就再次访问一下页面P,再将页面P和其中包含的资源文件保存下来,之前保存的页面P和其中包含的资源文件就没用了。

那上面两个流程是不是都有个重新下载的过程,那重要的就是如果感知到资源的变化。第一个例子中是老师通知同学们资源变化了,第二个例子就没有上面说的那么简单了,没人主动告诉小B它缓存的资源已经过时了,那小B是通过什么方式才能知道自己缓存的资源已经过时了呢:

缓存的状态

状态 备注
无缓存 初次见面,第一次进入某个页面
缓存过时/非法 重逢不识君,进入过某个页面,并有对应缓存,但再次进入时可能已经过时了
缓存合法 归来仍是少年,进入过某个页面,并有对应缓存,再次进入时仍然是有效的

而缓存状态的变化,需要浏览器和服务器之间达成某些协议,这些协议由HTTP头部确定及执行,这里我们介绍最常用的:

缓存头部

  • Cache-Control
  • Etag及If-None-Match夫妇
  • Last-Modified及If-Modified-Since夫妇

Cache-Control

Cache-Control的语法及使用姿势众多,刚兴趣的可直接到MDN查看,这里我们只介绍它在响应头中用于告知浏览器如何进行缓存行为:

Response Headers Cache-Control: public, max-age= 

上面的响应头中Cache-Control: public, max-age=告知浏览器从当前请求的时间点开始,再次请求此资源如果还未超过秒(1年),你就就不必问我了,放心地使用你本地保存的就好了。但是这就有个问题了,如果一年内的某天,此资源变化了,浏览器该如何知道呢?很遗憾,这种情况下浏览器在1年内是不会再知道了。

所以如果web server想要对资源设置诸如Cache-Control: public, max-age=响应头,一般需要前端搭配文件名hash来使用,这在各种构建工具或脚手架中一般都有相应的配置,如webpack配置:

// webpack module.exports = { 
    // ... output: { 
    filename: '[name].[contenthash].js', // filename: '[name].[hash].js', // ... }, optimization: { 
    moduleIds: 'hashed', // ... }, // ... } 

适用资源:基本所有的资源型文件(如js、css、图片、字体文件等)。

设置为1年没有什么其他含义,只是一个较大的时间区间而已。


当我们按照上面方式配置完成后,满心欢喜的去发布新版了,可是部署完成后再次访问,尴尬了,没有变化?这就要注意了,缓存对于html文件也是生效的,我知道这很显而易见,但是很多人容易忽略。

特别是对于单页面应用来说,我们一般只有一个index.html,在index.html中引入其他js、css等资源文件,上面的步骤只是对于index.html中引入的资源文件名中添加了hash,保证发布新版后这些引入的资源文件在浏览器缓存中不存在,但是如果浏览器取得index.html是通过本地缓存得到的呢?

<!-- 缓存内index.html --> <link rel="stylesheet" href="index.v1.css" /> <script src="index.v1.js"></script> 
<!-- 新版的index.html --> <link rel="stylesheet" href="index.v2.css" /> <script src="index.v2.js"></script> 

这里为了方便,我们使用v1、v2等代指hash

很显然,如果浏览器从缓存中获取index.html,然后肯定会尝试获取index.v1.cssindex.v1.js,而这两个文件再缓存中也是存在的且是合法的(假设还在1年内),那自然用户看到的页面及功能都是老的了。那我们应该如何为html文件设置缓存策略呢?

我们可以在web server的配置中针对html文件设置Cache-Control: no-cache,当我们请求html文件时,响应头会包含:

Response Headers Cache-Control: no-cache 

要特别注意no-cache(允许缓存,但是使用前要向服务器确定缓存是否合法,确定方案下面会讲到)和no-store(不允许缓存)的区别。

这样的话,当我们发布新版后,浏览器请求index.html发现有缓存,但并不会无脑的使用,而是会向服务器确认一下当前的缓存是否合法,如果合法则直接使用缓存内的版本,否则会向服务器请求最新的index.html,之后我们的index.v2.cssindex.v2.js就能够被正确获取,用户就能够看到新的页面了。

ETag及If-None-Match

ETag被称为实体标签或版本标识符,ETag变化代表资源的变化。

上面说道,浏览器有时需要向服务器确定缓存是否合法,那通过ETag响应头部和If-None-Match请求头部就能够确定,大致过程如下:

  1. 首次请求index.html,服务器响应头部中包含ETag: W/"v1"首部
  2. 浏览器缓存index.html,再次请求index.html时,请求头部包含If-None-Match: W/"v1"
  3. 服务器确定index.html是否有变化,如果没有变化,则状态码返回304,并且没有响应体,浏览器将使用本地缓存;如果有变化,则状态码返回200,将新的index.html传给浏览器,并返回新的ETag: W/"v2"首部
  4. 重复2、3

v1、v2只是为了标识出ETag的变化,实际上生成ETag的算法也不唯一,甚至简单地使用版本号也可以。关于ETag详细信息可参阅MDN。

Last-Modified及If-Modified-Since

除了ETagIf-None-Match,使用Last-ModifiedIf-Modified-Since组合也能起到类似的效果,大致过程和前组合类似:

  1. 首次请求index.html,服务器响应头部中包含Last-Modified: Wed, 21 Oct 2019 07:28:00 GMT首部
  2. 浏览器缓存index.html,再次请求index.html时,请求头部包含If-Modified-Since: Wed, 21 Oct 2019 07:28:00 GMT
  3. 服务器确定index.html是否有变化(通过资源的最近修改时间和Wed, 21 Oct 2019 07:28:00 GMT对比),如果没有变化,则状态码返回304,并且没有响应体,浏览器将使用本地缓存;如果有变化,则状态码返回200,将新的index.html传给浏览器,并返回新的Last-Modified: Wed, 22 Oct 2019 09:32:00 GMT首部
  4. 重复2、3

Last-ModifiedIf-Modified-Since组合准确度不如ETagIf-None-Match组合,所以不太推荐使用:

  • 有些服务器无法正确地判断资源的最近修改日期
  • 如果资源的变化周期在秒级以下,只能精确到秒的修改日期就不那么精确了

确定缓存是否合法,也会发请求和服务器端通信,但如果缓存有效,服务器发回的响应中是不包含响应体的,这样流量消耗是很小的,只有头部的消耗;即使缓存无效,也只是相当于发了一个首次请求而已。

总结

综上所述,我们可以在部署前端工程时使用如下方案,保证用户能够享受缓存带来的便利,也能保证不会因为缓存造成更新不生效的问题:

  • 针对大部分资源文件,使用Cache-Control: public, max-age=文件名hash的方案
  • 针对html文件,使用Cache-Control: no-cacheETag方案

为不同类型资源配置响应头部是web server的工作,请求头部是浏览器的自发工作,文件hash是前端的工作。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/233220.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 谷歌清楚缓存快捷键_清除浏览器缓存的快捷键是什么

    谷歌清楚缓存快捷键_清除浏览器缓存的快捷键是什么我们经常会遇到这些情况,浏览器打开网页打不开,打开网页老是在加载。尤其是在我们在做网页设计的时候,经常预览结果往往没有出现我们想要的结果,很大可能性就是浏览器的缓存没有清除引起的。那么清除浏览器缓存的快捷键是什么呢?佰佰安全网就带大家来了解一下这些打开浏览器,按Ctrl+Shift+Delete,就会出现清除浏览器缓存的框,你可以选择你要清理的东西,点击ok,一切搞定。各种浏览器清除缓存数据的方法…

    2022年7月18日
    15
  • 【ES6】Promise用法[通俗易懂]

    【ES6】Promise用法[通俗易懂]promise理解及使用Promise解决的问题——异步Promise的基本用法异步操作拒绝及中断调用链ES6对Promise/A+的扩展Promise.all的扩展Promise.race的扩展众所周知的,Javascript是一种单线程的语言,所有的代码必须按照所谓的“自上而下”的顺序来执行。本特性带来的问题就是,一些将来的、未知的操作,必须异步实现(关于异步,我会在另一篇文章里进行讨论)…

    2022年6月18日
    31
  • DOS 和 Linux 常用命令的对比

    DOS和Linux常用命令的对比许多在shell提示下键入的Linux命令都与你在DOS下键入的命令相似。事实上,某些命令完全相同。本附录提供了Windows的DOS提示

    2021年12月26日
    41
  • IsBackground对线程的重要作用「建议收藏」

    IsBackground对线程的重要作用「建议收藏」IsBackground对线程的重要作用要点:1、当在主线程中创建了一个线程,那么该线程的IsBackground默认是设置为FALSE的。2、当主线程退出的时候,IsBackground=FALSE的线程还会继续执行下去,直到线程执行结束。3、只有IsBackground=TRUE的线程才会随着主线程的退出而退出。4、当初始化一个线程,把T

    2022年10月10日
    2
  • Qt中的角度转弧度[通俗易懂]

    Qt中的角度转弧度[通俗易懂]在Qt中,qAsin(),qAtan2()等三角函数的返回值是弧度而不是角度,因此要将弧度转化为角度。弧度=角度*Pi/180以qAtan()函数为例qrealqAtan(qrealv)Returnsthearctangentofvasanangleinradians.Arctangentistheinverseoperationoftangent….

    2022年6月16日
    85
  • Iocomp Ultra Pack ActiveX 5.12[通俗易懂]

    Iocomp Ultra Pack ActiveX 5.12[通俗易懂]Iocomp的ActiveX/VCLStdPack是一个包含29个控件的套件,老版本Iocomp激活成功教程版很多,用于创建使用ActiveX或VCL开发环境的专业仪器应用程序。这些控件可用于科学、工程、医疗、石油和天然气、半导体、工厂自动化、航空航天、军事、机器人、电信、楼宇和家庭自动化、HMI、SCADA以及数百种其他类型的应用。所有Iocomp控件均启用OPC。如果您的项目需要OPC连接,您可以将任何属性连接到您的OPC项目/标签。所有连接都可以使用Iocomp属性编

    2022年7月25日
    8

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号