浏览器存储与缓存入门指南:前端性能优化的第一块基石

一、浏览器存储

浏览器存储是网页在用户本地保存数据的技术,主要用于实现数据持久化、减少服务器请求和离线访问能力。

1. localStorage(本地存储)

  • 存储特性:提供永久性存储,容量一般在5-10MB之间,以字符串形式存储键值对。
  • 生命周期:页面关闭后依然保留

2. sessionStorage(会话存储)

  • 存储特性:与localStorage类似,同样存储字符串类型的键值对,容量也在5-10MB左右。
  • 生命周期:其数据的有效期与页面会话相同,当页面会话结束(如浏览器关闭)时,数据将被清除。

3. cookies存储

  • 存储特性:存储带过期时间的键值对。由后端通过设置Max-Age来控制有效存储时长,存储在cookies中的内容会在浏览器发送请求时自动携带在请求头中。
  • 跨域能力:cookies具有跨域访问的能力,这使得它在不同域名间的数据交互中发挥作用。

4.indexedDB(客户端数据库)

  • 存储特性:是一种在浏览器端的数据库,理论上存储大小无上限,能够存储表结构以及复杂的数据类型。
  • 应用场景:适用于需要大量数据存储和复杂数据操作的Web应用,如离线应用、大型数据缓存等。

本地存储、会话存储、客户端数据库都不能跨域,cookies存储可以跨域。

特性 localStorage sessionStorage cookies indexedDB
数据类型 字符串 字符串 字符串/数值 复杂结构
存储容量 5-10MB 5-10MB ≤4KB 理论上无限
生命周期 永久 会话级 可设置过期 永久
自动发送请求
同源策略 严格限制 严格限制 支持跨子域 严格限制
数据操作方式 同步API 同步API 同步API 异步API
事务支持 不支持 不支持 不支持 完整支持
主要应用场景 静态数据存储 临时数据暂存 会话管理 大型结构化数据

二、浏览器缓存(也叫HTTP缓存)

什么是浏览器缓存?

浏览器缓存是浏览器将已请求资源(如HTML、CSS、图片)暂存本地的机制。通过复用本地副本,减少重复请求,显著提升页面加载效率并降低网络消耗。

将页面上长时间不更新的资源缓存到浏览器上,下次访问页面时,该部分资源直接从缓存中获取,从而减少了网络请求的次数,提高了页面的加载速度。

浏览器缓存数据实际存储在本地磁盘的缓存目录中(不同浏览器路径不同)。当用户使用强制刷新时,浏览器会跳过缓存校验机制。

1. 强缓存

  • 设置方式 :通过在响应头中设置Cache-Control字段,其值为max-age=xxx(单位为秒),来指定缓存的有效期。
  • 工作原理:当浏览器请求资源时,如果该资源仍在强缓存有效期内,则直接从浏览器的缓存中获取,无需向后端发送请求。
  • 局限性 :对于通过浏览器url地址栏直接请求的资源,请求头中会自动携带Cache-Control: max-age=0导致强缓存失效
rust 复制代码
const { ext } = path.parse(filePath); //获取文件后缀
res.writeHead(200, {
        'content-type': mime.getType(ext), // 根据文件后缀生成对应的content-type
        'cache-control': 'max-age=86400', // 强缓存 1 天
      })

在强缓存有效期内,浏览器直接使用本地缓存,不会产生任何网络请求,强缓存只有在失效的那一刻才会重新发起新的请求。

只用强缓存可以把除了url地址栏访问的资源存起来,但是后端资源更新了就无法第一时间让前端获取到, 所以还需要协商缓存

2. 协商缓存

为了解决强缓存的局限性,引入了协商缓存机制。协商缓存通过在浏览器和服务器之间交换特定的头部信息,来判断资源是否需要重新获取。

(1) Last-Modified 与 If-Modified-Since

浏览器第一次访问资源时,响应头中携带last-modified字段,该字段的值为资源的最后修改时间。当浏览器接收到响应头后,会在该资源再次被请求时,在请求头中自动携带 If-Modifed-Since字段

  • Last-Modified:服务器在响应头中携带该字段,值为资源的最后修改时间。浏览器在首次访问资源时会收到这个时间戳。
  • If-Modified-Since :当浏览器再次请求同一资源时,会在请求头中自动携带该字段,其值为上次收到的 Last-Modified 值。服务器收到请求后,会比较 If-Modified-Since 的值和资源当前的最后修改时间。如果两者一致,说明资源未被修改,服务器返回 304 状态码,浏览器从缓存中获取资源;如果不一致,则返回 200 状态码,浏览器就会重新获取最新资源。
javascript 复制代码
 const timeStamp = req.headers['if-modified-since'] //从请求头获取if-modified-since

      let status = 200
      if (timeStamp && Number(timeStamp) === stats.mtimeMs) { // 文件没有发生过更改
        status = 304
      }

      res.writeHead(status, {
        'content-type': mime.getType(ext),
        'cache-control': 'max-age=86400', // 强缓存 1 天
        'last-modified': stats.mtimeMs, // 最后修改时间
      })

      if (status === 200) {
        const readStream = fs.createReadStream(filePath); // 创建可读流
        readStream.pipe(res); // 将可读流的数据,通过管道,输出到前端
      } else {
        return res.end();
      }

(2)ETag 与 If-None-Match

  • ETag:也叫文件指纹。服务器根据资源的内容生成一个唯一的标识(如 MD5 哈希值),并在响应头中携带该字段。浏览器首次访问时会收到这个标识。
  • If-None-Match :浏览器再次请求时,在请求头中自动携带该字段,其值为上次收到的 ETag 值。服务器比较 If-None-Match 的值和当前资源的 ETag 值。如果一致,返回 304 状态码,浏览器从缓存中获取资源;如果不一致,返回 200 状态码和更新后的资源。
javascript 复制代码
const ifNoneMatch = req.headers['if-none-match'] // 从请求头获取if-none-match

      checksum.file(filePath, (err, sum) => {//MD5加密方式
        sum = `"${sum}"`

        if (ifNoneMatch === sum) {  // 文件没有变化

          res.writeHead(304, {
            'Content-Type': mime.getType(ext),
            'etag': sum,
          })
          res.end()

        } else {

          res.writeHead(200, {
            'Content-Type': mime.getType(ext),
            'Cache-Control': 'max-age=1000000',
            'etag': sum,
          })
          const resStream =  fs.createReadStream(filePath)
          resStream.pipe(res)

        }

以上代码中,引入checksum,计算文件的MD5值,同一份文件加密得到的MD5值是一样的,文件内容不一样,MD5值就不一样。把加密后得到的MD5值作为文件的标识Etag,并设置在响应头里面。

以上2种手段,用法基本一致,Last-Modified是以最后一次修改时间为标识,ETag是以文件内容作为标识。 MD5加密是要耗费性能的。大文件加密耗时比较久,所以一般采用last-modified + If-Modifed-Since 文件不是特别大且不易修改,采用文件指纹etag + If-None-Match

方案 优点 缺点
Last-Modified 计算成本低 内容不变但文件被修改也会重新发请求
ETag 内容级精确验证 大文件计算消耗资源

缓存优先级

强缓存优先:浏览器始终优先检查本地强缓存,若在有效期内(Cache-Control/Expires未过期),直接使用缓存且不发送任何请求

失效触发协商:仅当强缓存过期时,才携带验证头(If-Modified-Since/If-None-Match)发起协商请求

后端资源更新了,如何让前端第一时间拿到呢?

在资源文件名后添加哈希值(如:app.3a7b6c8d.js),当资源更新时,哈希值也会改变。这样,浏览器会将其视为不同的资源,从而重新请求和缓存。确保资源更新后URL变化,自动绕过旧缓存。

总结

HTTP 缓存机制是前端性能优化的重要组成部分。强缓存和协商缓存不是对立的,强缓存和协商缓存相辅相成,共同构成了高效的缓存策略。合理运用这些机制,可以显著提升网页的加载速度和用户体验,同时减轻服务器的负载压力。

相关推荐
qq. 28040339846 小时前
CSS层叠顺序
前端·css
喝拿铁写前端6 小时前
SmartField AI:让每个字段都找到归属!
前端·算法
猫猫不是喵喵.6 小时前
vue 路由
前端·javascript·vue.js
烛阴7 小时前
JavaScript Import/Export:告别混乱,拥抱模块化!
前端·javascript
bin91537 小时前
DeepSeek 助力 Vue3 开发:打造丝滑的表格(Table)之添加行拖拽排序功能示例12,TableView16_12 拖拽动画示例
前端·javascript·vue.js·ecmascript·deepseek
GISer_Jing7 小时前
[Html]overflow: auto 失效原因,flex 1却未设置min-height &overflow的几个属性以及应用场景
前端·html
牛马baby7 小时前
Java高频面试之并发编程-01
java·开发语言·面试
程序员黄同学7 小时前
解释 Webpack 中的模块打包机制,如何配置 Webpack 进行项目构建?
前端·webpack·node.js
拉不动的猪7 小时前
vue自定义“权限控制”指令
前端·javascript·vue.js
再学一点就睡7 小时前
浏览器页面渲染机制深度解析:从构建 DOM 到 transform 高效渲染的底层逻辑
前端·css