揭秘浏览器的默认缓存行为

前言

前提阅读本篇文章,你需要知道浏览器缓存(强缓存和协商缓存)以及http状态码相关前置知识,可以阅读这篇大佬的文章。juejin.cn/post/712719…

为什么想写这篇文章,是因为在有次在开发时,在我第一次请求html文件时,响应头只有协商缓存相关的字段ETag。当我刷新页面,再一次请求了html文件时,响应状态码为304。按现有逻辑来说,Cache-Control和Expires是设置资源的缓存时间。这两个响应头既然服务端都没有设置,那么浏览器就不会进行资源的缓存。但是状态码却为304,上面的第三点里讲到,304会从缓存里拿资源。当时查阅了网上的资料。有的说浏览器会默认缓存资源,有的说不会,众说纷纭。我决定亲自动手验证。

揭秘浏览器的默认缓存行为

我进行了如下思考:

1.即后端没有设置任何相关缓存的响应头的情况下,浏览器会默认给我们缓存资源吗?

2.如果默认有缓存行为,所有资源和文件都会默认给缓存吗(包括html js css 图片等)?

3.如果只设置协商缓存,不设置强缓存,会怎样?

4.如果有默认缓存行为,该如何禁止浏览器默认缓存(当然其实并没有什么意义,因为它是默认给缓存了,取决于我们用不用)

先简单说三点:

1.我们知道,在第一次请求时,如果服务端给某个资源设置了强缓存相关的响应头字段,在有效时间内,如果再次请求该资源,会走强缓存。如果缓存资源过期,浏览器就会携带相关的请求头,服务端判断资源是否更新,没有更新的话,服务端返回304。

揭秘浏览器的默认缓存行为

2.资源被缓存到内存还是磁盘中, 由浏览器自己决定。影响因素是资源的种类和大小。

3.http状态码是浏览器和http协议约定好的一种规范。浏览器根据不同的http状态码做不同的处理。

  • 服务端返回301或者302状态码,浏览器会自动重定向到具体的url。
  • 服务端还回304,浏览器会从缓存里拿资源。
  • ……

因为根据http版本的不同,强缓存和协商缓存分别都有两个属性控制。该文章是基于http1.0及以上的版本,强缓存我只拿Cache-Control举例,协商缓存只拿Last-Modified举例。

下面开始验证

准备工作

1.找到浏览缓存的缓存具体位置,以谷歌浏览器为例。

注意:只有缓存到硬盘的可以这样验证,缓存到内存的无法验证(比如几b大小的js文件,就会缓存到内存)

在浏览器中 输入chrome://version/

揭秘浏览器的默认缓存行为

图中标红路径下的Cache即为谷歌浏览器缓存

进来是这样的,都是之前网页缓存的

揭秘浏览器的默认缓存行为

全选删除 还剩下这五个文件(谷歌浏览器自带 无法删除)

揭秘浏览器的默认缓存行为

2.搭建简易node服务和前端

我用node搭了一个最简易的node服务,同时将html文件和静态资源与当前服务放在同一个目录下。目录和代码如下:

揭秘浏览器的默认缓存行为
index.html

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>

<body>
// 引用图片
<img src="./1.png" width="100" alt="">

</body>

<script>

</script>
<style>

</style>

</html>

server.js

const http = require('http')
const fs = require('fs')
const httpPort = 80

http.createServer((req, res) => {

}).listen(httpPort, () => {
console.log('Server listening on: http://localhost:%s', httpPort)
}
)

安装nodemon 监听代码的改动npm install nodemon -g

运行 nodemon sever.js

开始

1.验证静态资源是否被浏览器默认缓存

1.1验证图片

const http = require('http')
const fs = require('fs')
const httpPort = 80

http.createServer((req, res) => {
  // 返回html文件
  if (req.url === '/') {
    const data = fs.readFileSync('./index.html');
    res.end(data)
  }
  // 不设置任何缓存 直接返回图片
  if (req.url === '/1.png') {
    console.log('访问了1.png');
    const data = fs.readFileSync('./static/1.png');
    res.end(data)
  }

}).listen(httpPort, () => {
  console.log('Server listening on: http://localhost:%s', httpPort)
}
)

访问一下(访问之前记得按上面所述方法清楚浏览器缓存)

揭秘浏览器的默认缓存行为

看到浏览器请求了html文件和1.png,但是你会发现缓存文件中多了这么一条

揭秘浏览器的默认缓存行为

那么到底是给我缓存了html文件,还是缓存了图片呢

用记事本打开f_0000d1文件验证一下

揭秘浏览器的默认缓存行为
webp说明缓存的就是图片

如果你觉得这样没有说服力,我们再做一轮验证。控制变量法,不引用图片,只加载html文件,看看缓存文件夹里会多一个文件出来吗

会发现除了浏览器自带的,没有缓存其它任何东西。
揭秘浏览器的默认缓存行为

到此就足以说明,谷歌浏览器存在默认缓存行为,谷歌浏览器将图片缓存到了磁盘里

但是也有个有意思的现象,当设置了cache-control有值时,该图片资源在内存中也缓存了一份

揭秘浏览器的默认缓存行为

揭秘浏览器的默认缓存行为

那么现在只验证了图片资源,那么其它资源会被缓存吗?

1.2验证html文件、js脚本、css文件等其它资源

开头讲过,浏览器缓存资源的位置由浏览器自己决定,因此具有不确定性。经过验证,大多数情况下会把html文件、js脚本和css文件会缓存到内存中,偶尔也会缓存到磁盘中。

我只拿html文件举例(因为html文件有些不同之处)

if (req.url === '/') {
const data = fs.readFileSync('./index.html');
res.setHeader(
  'Cache-Control', 'max-age=1000'
);
res.end(data)
  }

给html设置了时长为1000s的缓存,会发现每次刷新页面,都会重新请求html。

磁盘中依旧是这五个默认文件

html设置缓存时间不生效,是浏览器的强制行为。因为html是头文件,即便给html文档设置缓存时间,也不会生效

揭秘浏览器的默认缓存行为
但是这只能说明没有从缓存里拿html文件,但是并不能说明没有对html文件进行缓存,虽然从磁盘中没看到html文件被缓存,但是有没有可能被缓存到内存中呢?

接下来我们验证html文件有没有被默认缓存,js脚本和css文件验证同理。

将html文件cache-control:no-cache(max-age=0效果一致),然后设置协商缓存,看看服务端是不是返回304。

if (req.url === '/') {
  // 获取请求头的if-modified-since
  const ifModifiedsince = req.headers['if-modified-since']
  const data = fs.readFileSync('./index.html')
  // 获取文件最后一次被修改的时间
  const { mtime } = fs.statSync('./index.html')
  // 如果ifModifiedsince有值并且等于当前文件最后一次被修改的时间,说明文件没有改动
  // 进入协商缓存,返回304,告诉用户资源没有过期,用户将从缓存中取资源
  if (ifModifiedsince == mtime.toUTCString()) {
    res.statusCode = 304
    res.end()
    return
  }
  res.setHeader('Cache-Control', 'no-cache')
  res.setHeader('Last-Modified', mtime.toUTCString())
  res.end(data)
  console.log('返回了资源')
}

揭秘浏览器的默认缓存行为

揭秘浏览器的默认缓存行为

因为res.end()没有还回任何数据,所以足以验证html文件也是从缓存里取得。

总结 浏览器也会默认缓存html js css。并且cahce-control:no-cache(max-age=0),这两个值都是设置缓存立马失效。在第一次请求时,浏览器将资源缓存,但是立马失效。所以再次请求时,如果html文件没被改动,服务端会返回304。

2.服务端不设置强缓存,只设置协商缓存的浏览器行为

上面已经验证过浏览器会自动缓存资源。

只设置协商缓存,在以后的请求中,每次都会去对比资源是否更改。服务端不设置强缓存相关字段,也就相当于上面讲的cache-control:no-cache(max-age=0)

感兴趣的可以自己去验证。

3.如何禁止浏览器缓存某个资源

服务端设置Cache-Control:no-store

if (req.url === '/1.png') {
  const data = fs.readFileSync('./static/1.png')
  res.setHeader('Cache-Control', 'no-store')
  res.end(data)
}

磁盘中没有新增,那如果缓存在内存中呢?我们再将服务端协商缓存设置上,看看会有什么样的行为。

揭秘浏览器的默认缓存行为

  1. 第一次请求时

揭秘浏览器的默认缓存行为

2.以后的请求

揭秘浏览器的默认缓存行为

揭秘浏览器的默认缓存行为

经过上面验证,说明Cache-Control:no-store浏览器不会缓存某个资源。

4.扩展

1.当资源缓存过期后,并不会从缓存中移除,并且当后端返回304,说明资源未被更新。资源又重新激活了,资源缓存的有效时间还是之前设置的缓存时间,在有效期内还是从缓存拿的资源。

2.在开发中,我们会发现服务端会给静态资源设置了强缓存,万一在资源缓存的有效期内,静态资源更新怎么办?

其实不用担心,前端打包会给静态资源文件名生成hash值,所以给静态资源的强缓存设置多少都没关系 一旦打包就会生成新的hash值,会请求新的文件。

3.缓存是把双刃剑,利用好的话,我们页面的性能与体验会提升。在开发中,我们如果担心缓存带来的影响,只需要将控制台的停用缓存勾上。每一次请求都会请求服务器。

揭秘浏览器的默认缓存行为

总结

  1. 浏览器存在默认缓存行为,任何资源会被默认缓存,用不用缓存取决于服务端和前端共同决定。
  2. 即便给html文件设置强缓存,也不会生效,每次请求都会请求服务端以确保html文件是最新的。
  3. 在http1.1版本中,给某资源响应头Cache-Control的值设置为no-store,可以禁止浏览器缓存该资源。

前端小白,一起交流学习。文中有不对的地方还请各位大佬多多指教~

原文链接:https://juejin.cn/post/7213642622074454073 作者:只会三步上篮

(0)
上一篇 2023年3月25日 上午11:10
下一篇 2023年3月25日 上午11:21

相关推荐

发表回复

登录后才能评论