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

一、浏览器存储

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

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

相关推荐
香蕉可乐荷包蛋4 小时前
浅入ES5、ES6(ES2015)、ES2023(ES14)版本对比,及使用建议---ES6就够用(个人觉得)
前端·javascript·es6
未来之窗软件服务4 小时前
资源管理器必要性———仙盟创梦IDE
前端·javascript·ide·仙盟创梦ide
冬瓜的编程笔记5 小时前
【八股战神篇】MySQL高频面试题
数据库·mysql·面试
liuyang___5 小时前
第一次经历项目上线
前端·typescript
西哥写代码6 小时前
基于cornerstone3D的dicom影像浏览器 第十八章 自定义序列自动播放条
前端·javascript·vue
独行soc6 小时前
2025年渗透测试面试题总结-百度面经(题目+回答)
运维·开发语言·经验分享·学习·面试·渗透测试·php
清风细雨_林木木6 小时前
Vue 中生成源码映射文件,配置 map
前端·javascript·vue.js
FungLeo6 小时前
node 后端和浏览器前端,有关 RSA 非对称加密的完整实践, 前后端匹配的代码演示
前端·非对称加密·rsa 加密·node 后端
不灭锦鲤6 小时前
xss-labs靶场第11-14关基础详解
前端·xss
不是吧这都有重名7 小时前
利用systemd启动部署在服务器上的web应用
运维·服务器·前端