什么是前端缓存

什么是前端缓存大家在日常的开发工作过程中 有没有遇到过下面几种情况 部署 发布前端工程后 增加的功能或修改的 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)
上一篇 2025年8月6日 下午1:01
下一篇 2025年8月6日 下午1:22


相关推荐

  • mysql INSTR函数用法

    mysql INSTR函数用法mysql 进行模糊查询时 可使用内部函数 instr 替代传统的 like 方式 并且速度更快 instr field str nbsp nbsp 函数 第一个参数 field 是字段 第二个参数 str 是要查询的串 返回串 str 的位置 没找到就是 0 nbsp 数据库表如下 1 执行如下 sqlselect frombookwher book name 经

    2026年3月18日
    1
  • C语言isalpha函数介绍、示例和实现

    C语言isalpha函数介绍、示例和实现文章目录 1 包含头文件 2 函数声明 3 功能说明 4 示例 5 自定义的 isalpha 函数的实现方法 6 版权声明 isalpha 函数用于判断字符是否为字母 a z 和 A Z 在本文中 我们先来介绍 isalpha 函数的使用方法 然后编写一个自定义的 isalpha 函数 实现与 isalpha 函数相同的功能 1 包含头文件 include ctype h 2 函数声明 intisal ctype h

    2026年3月19日
    2
  • 一个简单的WPF界面框架

    一个简单的WPF界面框架

    2021年6月11日
    62
  • 51单片机——LED点阵屏

    51单片机——LED点阵屏51 单片机 LED 点阵屏 LED 点阵屏 LED 点阵屏原理 74HC595 串转并芯片源代码例程一 静态笑脸效果展示例程二 笑脸 gt 平脸 gt 哭脸效果展示 LED 点阵屏 c51 的 LED 点阵屏其实就是一个 8 8 像素的屏幕 一共有 64 个发光二极管 如下图 LED 点阵屏原理其实这个原理和数码管是一样的 都是利用人的视觉暂留 不停扫描 LED 点阵 依次点亮每一盏灯 只不过数码管扩引脚采用的是 38 译码器 点阵屏采用的是 74HC595 串转并芯片 参考数码管原理图如下 他的 DPa DPh 其实就是上图的 D0 D7

    2026年3月18日
    1
  • jmeter性能测试实战视频(常用性能测试工具有哪些)

    1、搭建AUT第一步、下载所需文件   Discuz_***.zip——源码程序文件,是PHP程序,简体中文GBK的下载地址是http://download.comsenz.com/DiscuzX/3.1/Discuz_X3.1_SC_GBK.zip。   PHPnow_***.zip—–集成了Apache中间件、Mysql数据库和PHP,下载地址是http://servkit.

    2022年4月10日
    38
  • Vue slot-scope的理解(适合初学者)

    Vue slot-scope的理解(适合初学者)百度上已经有很多的关于 slot scope 的文章 但我感觉都是那些以前没学好 又回头学的人 他们都使用了 Vue 文件 我觉得有点不适合初学者 所以我就写一篇适合初学者的 先抛例程 lt DOCTYPEhtml gt lt htmllang en gt lt head gt lt metacharset UTF 8 gt

    2026年3月20日
    2

发表回复

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

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