首页 > 其他分享 >记录--关于浏览器缓存策略这件事儿

记录--关于浏览器缓存策略这件事儿

时间:2023-07-10 17:56:30浏览次数:40  
标签:缓存 const filePath -- fs path 浏览器

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

前言

我们打开百度这个网站并刷新多次时时,注意到百度的logo是没有每次都加载一遍的。我们知道图片是img标签中的src属性加载出来的,这也需要浏览器去请求图片资源的,那么为什么刷新多次浏览器只请求了一次图片资源呢?这就涉及到了浏览器的缓存策略了,这张图片被浏览器缓存下来了!

正文

一、为什么要有浏览器的缓存策略?

  • 提升用户体验,减少页面重复的http请求

二、为什么通过浏览器url地址栏访问的html页面不缓存?

  • 强制刷新页面浏览器url地址栏访问资源 时,浏览器默认会在请求头中设置Cache-control: no-cache,如设置该属性浏览器就会忽略响应头中的 Cache-control

如何优化网络资源请求的时间呢?有以下三种方式。

三、CDN网络分发

CDN:CDN会通过负载均衡技术,将用户的请求定向到最合适缓存服务器上去获取内容。

比如说,北京的用户,我们让他访问北京的节点,深圳的用户,我们让他访问深圳的节点。通过就近访问,加速用户对网站的访问,进而解决Internet网络拥堵状况,提高用户访问网络的响应速度。

四、强缓存

强缓存是浏览器的缓存策略,后端设置响应头中的属性值就能设置文件资源在浏览器的缓存时间过了缓存的有效期再次访问时,文件资源需再次加载

强缓存有两种方式来控制资源被浏览器缓存的时长:

  1. 后端设置响应头中的 Cache-control: max-age=3600 来控制缓存时长(为一个小时)
  2. 后端设置响应头中的 Expires:xxx 来控制缓存的截止日期(截止日期为xxx)

我们直接上代码让你更好理解,我们需要实现一个页面,页面上需展现一个标题一张图片

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <h1>Earth</h1>
  <img src="assets/image/earth.jpeg" alt="">
</body>
</html>

 

const http = require('http');
const path = require('path');
const fs = require('fs');
const mime = require('mime'); //接收一个文件后缀,返回一个文件类型

const server = http.createServer((req, res) => {
  const filePath = path.resolve(__dirname, `www/${req.url}`)  //resolve合并地址
  if (fs.existsSync(filePath)) {  //判断路径是否有效
    const stats = fs.statSync(filePath)   //获取文件信息
    const isDir = stats.isDirectory()    //是否是文件夹
    if (isDir) {
      filePath = path.join(filePath, 'index.html')
    }
    //读取文件
    if (!isDir || fs.existsSync(filePath)) {

      //判断前端请求的路径的后缀名是图片还是文本
      const { ext } = path.parse(filePath)  //.html .jpeg

      const time = new Date(Date.now() + 3600000).toUTCString()   //定义时间 作为缓存时间的有效期

      let status = 200

      res.writeHead(status, {
        'Content-Type': `${mime.getType(ext)};charset=utf-8`,
        'Cache-control': 'max-age=3600',  //缓存时长为一小时 
        // 'expires': time  //截止日期 缓存一小时后过期
      })

      if (status === 200) {
        const fileStream = fs.createReadStream(filePath)   //将文件读成流类型
        fileStream.pipe(res) //将文件流导入响应体
      }else{
        res.end();
      }

    }
  }
})

server.listen(3000, () => {
  console.log('listening on port 3000');
})

第一次运行:

 刷新页面后,可以看到图片资源没有重新加载:

三、协商缓存

我们想象这样的场景:当我们偷偷把图片偷偷换成另一张图片,图片名依然和之前那张一样,会是什么结果呢?
操作后,刷新页面发现图片还是之前那张图片,并没有换成新的!那这就出事儿了,后端图片换了,用户看到的还是老图片,有一种方案是改变图片资源的名字,直接请求最新图片资源,但这并不是最优方案,终极方案是需要协商缓存的帮忙。

协商缓存也是浏览器的缓存策略,它也有两种方式辅助强缓存,来判断文件资源是否被修改

1. 后端设置响应头中的 last-modified: xxxx

  • 辅助强缓存,让URL地址栏请求的资源也能被缓存
  • 辅助强缓存,借助请求头中的if-modified-since来判断资源文件是否被修改,如果被修改则返回新的资源,否则返回304状态码,让前端读取本地缓存

代码如下:

const http = require('http');
const path = require('path');
const fs = require('fs');
const mime = require('mime'); //接收一个文件后缀,返回一个文件类型

const server = http.createServer((req, res) => {
  const filePath = path.resolve(__dirname, `www/${req.url}`)  //resolve合并地址
  if (fs.existsSync(filePath)) {  //判断路径是否有效
    const stats = fs.statSync(filePath)   //获取文件信息
    const isDir = stats.isDirectory()    //是否是文件夹
    if (isDir) {
      filePath = path.join(filePath, 'index.html')
    }
    //读取文件
    if (!isDir || fs.existsSync(filePath)) {

      //判断前端请求的路径的后缀名是图片还是文本
      const { ext } = path.parse(filePath)  //.html .jpeg

      const time = new Date(Date.now() + 3600000).toUTCString()   //定义时间 作为缓存时间的有效期

      const timeStamp = req.headers['if-modified-since']  //请求头的if-modified-since字段
      let status = 200

      //判断文件是否修改过
      if (timeStamp && Number(timeStamp) === stats.mtimeMs) {  //timeStamp为字符串 转换为number类型判断
        status = 304
      }

      res.writeHead(status, {
        'Content-Type': `${mime.getType(ext)};charset=utf-8`,
        'Cache-control': 'max-age=3600',  //缓存时长为一小时   //max-age=0或no-cache不需要缓存
        // 'expires': time  //截止日期 缓存一小时后过期
        'last-modified': stats.mtimeMs //文件最后一次修改时间
      })

      if (status === 200) {
        const fileStream = fs.createReadStream(filePath)   //将文件读成流类型
        fileStream.pipe(res) //将文件流导入响应体
      }else{
        res.end();
      }

    }
  }
})

server.listen(3000, () => {
  console.log('listening on port 3000');
})
我们只要对比last-modified的值和if-modified-since的值有无变化即可:

2. Etag:文件的标签

  • 请求头中会被携带If-None-Match
  • Etag保证了每一个资源是唯一的,资源变化都会导致Etag变化。服务器根据If-None-Match值来判断是否命中缓存。 当服务器返回304的响应时,由于ETag重新生成过,response header中还会把这个ETag返回,即使这个ETag跟之前的没有变化。

 代码如下:

const http = require('http');
const path = require('path');
const fs = require('fs');
const mime = require('mime'); //接收一个文件后缀,返回一个文件类型
const md5 = require('crypto-js/md5');

const server = http.createServer((req, res) => {
  const filePath = path.resolve(__dirname, `www/${req.url}`)  //resolve合并地址
  if (fs.existsSync(filePath)) {  //判断路径是否有效
    const stats = fs.statSync(filePath)   //获取文件信息
    const isDir = stats.isDirectory()    //是否是文件夹
    if (isDir) {
      filePath = path.join(filePath, 'index.html')
    }
    //读取文件
    if (!isDir || fs.existsSync(filePath)) {

      //判断前端请求的路径的后缀名是图片还是文本
      const { ext } = path.parse(filePath)  //.html .jpeg
      const content = fs.readFileSync(filePath);
      let status = 200

      //判断文件是否被修改过
      if (req.headers['if-none-match'] == md5(content)) {
        status=304
      }

      res.writeHead(status, {
        'Content-Type': `${mime.getType(ext)};charset=utf-8`,
        'Cache-control': 'max-age=3600',  //缓存时长为一小时   //max-age=0或no-cache不需要缓存
        'Etag': md5(content)  //文件资源的md5值
      })

      if (status === 200) {
        const fileStream = fs.createReadStream(filePath)   //将文件读成流类型
        fileStream.pipe(res) //将文件流导入响应体
      } else {
        res.end();
      }

    }
  }
})

server.listen(3000, () => {
  console.log('listening on port 3000');
})

最后附上一张图便于更好理解浏览器的缓存策略:

 

本文转载于:

https://juejin.cn/post/7253675120010199101

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 

标签:缓存,const,filePath,--,fs,path,浏览器
From: https://www.cnblogs.com/smileZAZ/p/17541866.html

相关文章

  • 《ReAct: SYNERGIZING REASONING AND ACTING IN LANGUAGE MODELS》论文学习
    一、论文主要思想本文首先认为,到目前为止,LLM在语言理解方面令人印象深刻,它们已被用来生成CoT(思想链)来解决一些问题,它们也被用于执行和计划生成。尽管这两者是分开研究的,但本文旨在以交错的方式将推理和行动结合起来,以提高LLM的表现。这个想法背后的原因是,如果你考虑一下作为......
  • java判断json格式的方法
    ​  在Java中,您可以使用不同的库来检查和验证JSON。以下是使用两个常用的JSON库(Jackson和Gson)来检查JSON的示例代码:使用Jackson库:importcom.fasterxml.jackson.core.JsonParseException;importcom.fasterxml.jackson.databind.JsonNode;importcom.fasterxml.jack......
  • 关于抽象类和抽象方法的使用
    1、抽象类并不能直接new抽象类对象2、子类必须覆盖抽象类中的所有方法进行使用。3、抽象类不能创建对象,是特殊的存在,是“抽象”抽出相同的部分。4、抽象类可以有构造方法,并且在执行子类时还要先于子类的构造方法去执行,符合java运行的规则 ......
  • java判断json格式的方法
    ​   在Java中,您可以使用不同的库来检查和验证JSON。以下是使用两个常用的JSON库(Jackson和Gson)来检查JSON的示例代码:使用Jackson库:importcom.fasterxml.jackson.core.JsonParseException;importcom.fasterxml.jackson.databind.JsonNode;importcom.fasterxml.ja......
  • readability-lxml 源码解析(一)
    browser.pydefopen_in_browser(html):"""OpentheHTMLdocumentinawebbrowser,savingittoatemporaryfiletoopenit.Notethatthisdoesnotdeletethefileafteruse.Thisismainlymeantfordebugging."......
  • 字典和json格式的对比
    #字典和json格式的对比p_dict={'name':'fqs','age':18}p_json='{"name":"fqs","age":18}'#1将字典转为json格式importjsonresult1=json.dumps(p_dict)print(result1,type(result1))'''{"na......
  • AI时代ToB企业何去何从
    最近,“百模大战”打得如火如荼,各大科技公司都纷纷推出自己的通用大模型,以及结合行业特性的行业大模型。科技行业的一片繁荣的景象,甚至一度让人暂时忘记了大厂裁员潮的凶猛。行业大模型受到前所未有的关注,这意味着人工智能在ToB行业的应用将进一步深化。通过运用行业大模型,企业......
  • maven中sqljdbc4.jar无法下载的正确解决办法
     在pom.xml中添加如下依赖是无法导入sqljdbc4.jar包的,maven会报错,找不到依赖,无法下载 我们上maven仓库能够发现有这么一句提示 thisartifactislocatedat Clojars repository(https://clojars.org/repo/)这个jar包是在Clojars仓库里面,我们需要在pom.xml中加上如下......
  • SQ工具|5|界址点顺时针编号
    顺时针编码工具,是以宗地节点的起始点为起点,沿着宗地的边,对界址点进行顺时针编码。点击顺时针编码工具,界面如图所示 界面介绍:1.首尾去重,当同属于一个宗地的界址点在起始点存在重复时,勾选此功能可去除重复点编号。2.编号带'J':为界址点的编号前加一个'J'字符。3.内环顺时针:......
  • aardio桌面软件开发 简单,打包后文件小,支持 .net python 和 众多插件
    aardio编程语言-官网 aardio ......