Skip to content

在浏览器中,Web Storage 提供了多种数据存储方式,每种方式适用于不同的场景和需求。以下是浏览器中常见的存储方式及其简介:


浏览器存储

下面给出各类浏览器存储方案的简介及对比,帮助你了解它们各自的特点和适用场景。


1. Web Storage

localStorage

  • 简介:以键值对方式存储数据,数据持久存在(除非手动清除),即使浏览器关闭后仍然存在。
  • 特点
    • 持久存储:数据会永久保存在浏览器中,除非显式清除(如手动清理缓存或使用localStorage.clear())。
    • 大小限制:大约 5MB(各浏览器略有不同)。
    • 作用域:同源策略(同协议、同域名、同端口下的页面共享)。
  • API 示例
javascript
// 存储数据
localStorage.setItem('key', 'value');

// 获取数据
const value = localStorage.getItem('key');

// 删除数据
localStorage.removeItem('key');

// 清空所有数据
localStorage.clear();
  • 应用场景
    • 存储不敏感的持久数据(如主题偏好、语言设置)。
    • 注意:由于暴露在 JavaScript 中,不适合存储敏感信息(如 Token)。

sessionStorage

  • 简介:与 localStorage 类似,但数据仅在当前会话(当前标签页或窗口)内有效,关闭标签页后即清除。
  • 特点
    • 会话级存储:数据仅在当前浏览器标签页或窗口中有效,关闭页面即失效
    • 大小限制:大约 5MB
    • 作用域:同源策略,同标签页/窗口内共享,但不同标签页互不影响。
  • API 示例
javascript
// 存储数据
sessionStorage.setItem('key', 'value');

// 获取数据
const value = sessionStorage.getItem('key');

// 删除数据
sessionStorage.removeItem('key');

// 清空所有数据
sessionStorage.clear();
  • 应用场景

    • 存储短期使用的数据(如临时表单数据,会话级状态)。
    • 防止跨标签页共享(如银行系统中的登录状态管理)。

2. Extension Storage

  • 简介:浏览器扩展(插件)提供的存储 API,如 Chrome 的 chrome.storage.localchrome.storage.sync
  • 特点
    • 异步 API,支持存储扩展专用数据
    • storage.sync 可在设备间同步数据(有配额限制),storage.local 则只在本地存储
  • 典型用途:扩展配置、用户设置、扩展内部状态

3. IndexedDB

  • 简介:浏览器内置的 NoSQL 数据库,用于存储大量结构化数据。

  • 特点

    • 基于事务的 NoSQL 数据库,支持大量结构化数据的存储。
    • 异步 API,避免阻塞主线程。
    • 大小限制:通常为 50MB 以上(取决于浏览器)。
  • API 示例

    javascript
    // 打开数据库
    const request = indexedDB.open('MyDatabase', 1);
    
    request.onsuccess = (event) => {
      const db = event.target.result;
    
      // 创建事务
      const transaction = db.transaction(['users'], 'readwrite');
      const store = transaction.objectStore('users');
    
      // 添加数据
      store.add({ id: 1, name: 'John Doe' });
    };
  • 应用场景

    • 存储大量结构化数据(如离线应用、缓存数据、PWA)。
    • 离线模式下的数据存储、文件存储(如 Google Docs 离线版)。

  • 简介:小型键值对数据存储机制,数据会自动随 HTTP 请求发送给服务器。

  • 特点

    • 大小限制:每个 Cookie 大小约 4KB
    • 作用域:可设置作用路径和域名,支持跨页面共享。
    • 自动携带:每次请求会自动携带到服务器(除非设置 HttpOnly)。
    • 支持过期时间:可以设置 ExpiresMax-Age 控制存储时长。
  • API 示例

javascript
// 设置 Cookie
document.cookie = "username=JohnDoe; expires=Fri, 31 Dec 2025 12:00:00 UTC; path=/";

// 读取 Cookie
console.log(document.cookie);

// 删除 Cookie
document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
  • 安全性设置

    • HttpOnly:禁止 JavaScript 访问(防止 XSS)。
    • Secure:仅通过 HTTPS 传输。
    • SameSite:限制跨站请求携带(防止 CSRF)。
  • 应用场景

    • 存储会话信息(如 Session ID)。
    • 实现用户偏好设置(如语言选择)。

5. Private State Tokens

  • 简介:一种正在探索中的 API/机制,旨在存储用户状态的令牌,但对外提供的是模糊或私有的信息,从而保护用户隐私。
  • 特点
    • 设计上避免精确跟踪和指纹识别
    • 用于实现无痕但可验证的状态存储,适用于广告转化、欺诈防范等场景
  • 典型用途:隐私保护下的状态保存和跨会话标识,目前仍处于实验或标准制定阶段

6. Interest Groups

  • 简介:由浏览器(如在 Privacy Sandbox 提案中)管理的用户兴趣组,网站或广告平台可以建议用户加入某些兴趣组。
  • 特点
    • 数据在浏览器内部存储,不直接暴露给网站
    • 目的是用于目标广告展示,而不泄露个人详细浏览信息
  • 典型用途:实现隐私友好的广告定向与统计

7. Shared Storage

  • 简介:一种提议中的 API,用于在多个来源之间共享汇总数据,但不会泄露用户的个人信息。
  • 特点
    • 支持跨域或跨会话的聚合数据存储
    • 主要用于广告转化、归因分析等场景
  • 典型用途:跨站点数据聚合与无痕数据共享,目前正处于探索阶段

8. Cache Storage(Service Worker)

  • 简介:Service Worker 提供的缓存 API,用于缓存网络请求及响应,实现离线访问和提高加载性能。

  • 特点

    • 允许通过 Service Worker 缓存 HTTP 请求和响应。
    • 支持离线访问和缓存策略(如 PWA)。
  • API 示例

    javascript
    // 缓存请求
    caches.open('my-cache').then((cache) => {
      cache.add('/index.html');
    });
    
    // 读取缓存
    caches.match('/index.html').then((response) => {
      if (response) {
        console.log('Cache hit:', response);
      }
    });
  • 应用场景

    • Progressive Web Apps(PWA)离线缓存。
    • 静态资源的缓存管理(如图片、CSS、JS)。

9. Storage Buckets

  • 简介:一种较新的概念,用于将存储空间分区(或“桶化”),以便隔离和管理不同网站或应用的数据。
  • 特点
    • 每个“桶”相互独立,有助于防止跨站跟踪和数据混用
    • 可能与隐私增强措施相关,例如分区存储数据以减少浏览器指纹信息
  • 典型用途:隐私友好的存储隔离、跨域数据隔离管理,目前正处于实验或提案阶段

总体对比

存储方案存储方式数据持久性数据容量访问方式典型用途安全性/隐私关注点
localStorage键值对持久(手动清除)~5MB同步 API用户偏好、主题设置易受 XSS,适合非敏感数据
sessionStorage键值对会话级(标签页内)~5MB同步 API临时数据、单会话状态隔离性较好,但仅限当前标签页
Extension Storage键值对/对象持久或同步配额较低/中等异步 API扩展配置、状态管理数据仅限扩展内部,安全性由扩展平台保证
IndexedDBNoSQL 数据库持久大量数据异步 API离线应用、复杂数据存储复杂数据存储,需注意数据结构设计
Cookie键值对可配置(会话或持久)~4KB/个同步(自动随请求)会话管理、身份验证、跟踪易受 CSRF,需正确配置安全标记(HttpOnly、Secure)
Private State Tokens令牌设计上持久或短期小(令牌形式)浏览器内部机制隐私保护状态存储、广告转化与欺诈防范目标是保护隐私,防止精确跟踪,目前仍在探索
Interest Groups浏览器管理数据持久(内部管理)小到中浏览器隐式管理广告定向、用户兴趣归类数据不直接暴露,侧重隐私保护下的广告统计
Shared Storage聚合存储设计上持久或聚合视实现而定提议中的 API跨域聚合数据、广告归因隐私友好,数据聚合后不关联个人信息,目前处于提案阶段
Cache Storage请求/响应缓存持久(开发者管理)较大(受配额限制)异步 API静态资源缓存、离线支持、性能优化数据仅限缓存用途,与业务数据分离
Storage Buckets分区存储持久或会话级分区配额浏览器内部管理数据隔离、隐私增强、跨域数据隔离设计上防止跨站跟踪,隔离不同网站数据,目前正处于探索阶段

总结

  • Web Storage(localStorage、sessionStorage) 提供简单的键值存储,适合存储小型、非敏感数据;localStorage 数据持久,而 sessionStorage 仅限当前会话。
  • Extension Storage 专为浏览器扩展设计,支持异步存储和跨设备同步(部分场景)。
  • IndexedDB 适合存储大量、复杂结构数据,是构建离线应用和大数据缓存的首选方案。
  • Cookie 由于每次请求都会自动携带,常用于会话管理与身份验证,但容量有限且安全性依赖配置。
  • Private State Tokens、Interest Groups、Shared Storage、Storage Buckets 等均属于新一代提案或正在探索的 API,旨在在隐私保护下实现数据存储、用户分组与跨站数据聚合,主要针对广告、归因与防跟踪场景。
  • Cache Storage 则专注于资源缓存,提升页面加载和离线体验。

每种存储方式都有其设计初衷和适用场景,开发者应根据数据敏感性、存储量、访问模式和安全要求来选择最合适的方案。

存储方案详解

Web Storage

localStorage

localStorage 是 Web 浏览器提供的本地存储机制之一,它允许网站将数据以键值对的形式存储在用户的浏览器中,数据即使在页面关闭或浏览器重启后仍然存在,除非被手动清除。


1. localStorage 基本特点

特性详情
存储类型键值对存储(key-value
数据持久性持久化存储,除非显式清除,否则不会过期
大小限制通常约 5MB(不同浏览器有差异)
作用域同源策略:相同协议 + 域名 + 端口的页面共享数据
自动携带到服务器?不会随 HTTP 请求自动发送
可否被 JavaScript 访问?可以,但容易受到 XSS 攻击
适合场景存储非敏感的持久数据(如用户设置、主题、偏好等)

2. localStorage 常用 API

方法描述
localStorage.setItem(key, value)存储数据(键值对)。值会被转换为字符串。
localStorage.getItem(key)读取指定 key 的数据。
localStorage.removeItem(key)删除指定 key 的数据。
localStorage.clear()清空当前域下的所有 localStorage 数据。
localStorage.key(index)获取指定索引的 key 名称。
localStorage.length获取当前存储的键值对数量。

3. localStorage 示例代码

a. 基本操作
javascript
// 存储数据
localStorage.setItem('username', 'JohnDoe');

// 获取数据
const user = localStorage.getItem('username');
console.log(user); // 输出: JohnDoe

// 更新数据
localStorage.setItem('username', 'JaneDoe');

// 删除数据
localStorage.removeItem('username');

// 清空所有数据
localStorage.clear();

b. 存储和读取对象(使用 JSON)

localStorage 只能存储字符串,若要存储对象或数组,需要使用 JSON.stringify()JSON.parse()

javascript
// 存储对象
const user = {
  name: "John",
  age: 30,
  role: "admin"
};

localStorage.setItem('user', JSON.stringify(user));

// 读取对象
const storedUser = JSON.parse(localStorage.getItem('user'));
console.log(storedUser.name); // 输出: John

4. localStorage 的使用场景

  1. 主题/偏好设置:保存用户选择的主题、语言等。
  2. 表单数据缓存:避免刷新页面时丢失表单内容。
  3. 简单的客户端状态管理:如购物车数据、分页设置等。
  4. 缓存数据:缓存一些非敏感的 API 数据减少请求。
  5. SPA(单页面应用):保存 JWT Token(⚠️ 不推荐,见下方安全问题)。

5. localStorage 安全性问题

a. 容易受到 XSS 攻击
  • 问题localStorage 的数据完全暴露给 JavaScript,如果网站存在 XSS 漏洞,攻击者可以通过注入的恶意代码窃取 localStorage 中的数据。
  • 风险:如果将 敏感数据(如 JWT Token、用户信息)存储在 localStorage 中,可能被窃取。
b. 无法防止 CSRF
  • 原因:虽然 localStorage 不会自动随请求发送,但如果使用 JavaScript 手动读取 localStorage 中的 Token 并加到请求头中,仍可能受到 CSRF 攻击。
c. 无加密保护
  • 问题localStorage 中的数据是明文存储的,任何有权限访问设备的人都可以在浏览器控制台查看数据。

6. localStorage 与其他存储方式对比

存储方式大小限制生命周期可被 JS 访问自动发送到服务器安全性
localStorage~5MB持久(需手动清除)易受 XSS 攻击
sessionStorage~5MB当前标签页关闭即失效相对更安全(仅当前标签页)
Cookie~4KB可配置(可持久/会话)✅(非 HttpOnly)易受 CSRF,需防护
IndexedDB>50MB持久(需手动清除)适用于大量数据存储

7. localStorage 最佳实践

  1. 避免存储敏感信息:如用户密码、JWT Token 等。
  2. 使用 JSON 存储复杂数据:使用 JSON.stringify()JSON.parse()
  3. 清理无用数据:避免长期存储过期数据。
  4. 使用事件监听器同步多标签页数据
跨标签页数据同步
javascript
// 监听 localStorage 的变化
window.addEventListener('storage', (event) => {
  if (event.key === 'theme') {
    console.log(`主题被更改为: ${event.newValue}`);
  }
});

当在一个标签页中更改了 localStorage 中的数据,其他标签页会通过 storage 事件监听到该变化。


8. localStorage 在真实项目中的使用示例

a. 主题切换示例(浅色/深色模式)
html
<button id="toggle-theme">切换主题</button>

<script>
  const btn = document.getElementById('toggle-theme');

  // 检查用户的主题偏好
  const currentTheme = localStorage.getItem('theme') || 'light';
  document.body.className = currentTheme;

  btn.addEventListener('click', () => {
    const newTheme = document.body.className === 'light' ? 'dark' : 'light';
    document.body.className = newTheme;

    // 存储主题偏好
    localStorage.setItem('theme', newTheme);
  });
</script>

<style>
  body.light { background-color: white; color: black; }
  body.dark { background-color: black; color: white; }
</style>

刷新或关闭页面后仍然保持用户上次选择的主题。


9. 什么时候不使用 localStorage?

  1. 不存储敏感数据(如 Token、个人身份信息)。
  2. 不用于高频读写操作(如聊天消息、实时数据等)。
  3. 避免存储大规模数据(使用 IndexedDB 替代)。
  4. 对于跨域需求,localStorage 不能跨域共享(使用服务器存储或其他方案)。

总结

  • localStorage 是简单易用的本地持久化存储方案,适合存储非敏感的持久性数据。
  • 对于安全性要求高的项目,不要将敏感信息存储在 localStorage
  • sessionStorageCookieIndexedDB 等存储机制可根据具体场景搭配使用。

sessionStorage

sessionStorage 是 Web Storage API 的一部分,用于在浏览器中存储数据,具有以下主要特点:


1. 基本特性

  • 会话级存储
    数据仅在当前浏览器标签页或窗口中有效,一旦标签页关闭,数据即被清除。
    → 适合用于存储临时数据,比如单次会话的表单输入或验证信息。

  • 同源策略
    仅在同一协议、域名和端口下的页面中共享数据,不同标签页间独立存储数据,即使它们属于同一网站。

  • 容量限制
    通常约 5MB(与 localStorage 类似,但具体大小因浏览器而异)。

  • 同步 API
    其操作为同步执行,适合存储少量数据,不适用于大量数据或高频读写场景。


2. 常用 API

  • 存储数据
javascript
sessionStorage.setItem('key', 'value');
  • 读取数据
javascript
const data = sessionStorage.getItem('key');
  • 删除指定数据
javascript
sessionStorage.removeItem('key');
  • 清空所有数据
javascript
sessionStorage.clear();

3. 使用场景

  • 临时状态存储
    例如,存储用户在单次会话中的操作数据、临时表单内容或一次性令牌,确保数据在标签页关闭后自动清除。

  • 防止跨标签页共享敏感数据
    如果你希望数据仅限于当前标签页,避免在用户打开多个标签时自动共享,可以使用 sessionStorage。

  • 单页面应用(SPA)中的临时数据
    在 SPA 中存储当前页面状态、分页信息或其他短期数据。


4. 与 localStorage 的区别

特性sessionStoragelocalStorage
生命周期当前标签页/窗口有效,关闭即失效持久存储,除非主动清除
跨标签页共享不共享,每个标签页独立同源下所有标签页共享
适用场景临时数据、会话状态用户偏好设置、长期缓存数据

5. 注意事项

  • 安全性
    和 localStorage 一样,sessionStorage 数据也暴露给 JavaScript,因此不建议存储敏感信息。

  • 同步执行
    操作 sessionStorage 时是同步的,频繁的大数据读写可能会阻塞主线程。


通过合理使用 sessionStorage,可以有效管理单次会话内的临时数据,提供更好的用户体验,同时保证数据不会跨标签页共享。

IndexedDB

IndexedDB 是浏览器内置的一种 NoSQL 数据库,用于在客户端存储大量结构化数据。与传统的键值存储(如 localStorage 和 sessionStorage)相比,IndexedDB 提供了更高的存储容量和更丰富的数据操作能力,适合构建离线应用和大数据缓存。


1. 主要特性

  • 异步 API
    IndexedDB 的所有操作都是异步执行的,这意味着它不会阻塞主线程,非常适合处理大量数据或频繁的读写操作。

  • 事务性存储
    支持事务处理,保证数据一致性,允许在一次操作中同时进行多个数据读写操作。

  • 面向对象存储
    数据以对象存储(Object Store)的形式保存,不需要将数据序列化为字符串,能够存储 JavaScript 对象、Blob、File 等类型。

  • 索引支持
    允许为存储的数据创建索引,以便更高效地查询数据。

  • 较大的存储容量
    通常能存储几十 MB 甚至更多数据,具体容量视浏览器实现和用户磁盘空间而定。


2. 基本概念

  • 数据库(Database)
    IndexedDB 存储数据的最高层级单位,一个数据库可以包含多个对象存储。

  • 对象存储(Object Store)
    类似于关系数据库中的表,每个对象存储用于存放具有相似结构的数据。

  • 主键(Key)
    每个存储在对象存储中的数据项都需要一个唯一的键,可以自动生成或由用户指定。

  • 索引(Index)
    为对象存储中的数据创建额外的查找途径,便于基于某些属性进行查询。

  • 事务(Transaction)
    数据操作需要在事务中执行,以保证数据的一致性和完整性。


3. 基本操作示例

下面是一个简单的 IndexedDB 示例,展示如何打开数据库、创建对象存储、添加和读取数据:

javascript
// 打开(或创建)数据库
const request = indexedDB.open('MyDatabase', 1);

request.onupgradeneeded = (event) => {
  const db = event.target.result;
  // 创建一个对象存储,指定 keyPath 为 'id'
  const objectStore = db.createObjectStore('users', { keyPath: 'id' });
  
  // 为对象存储创建索引(例如,按姓名查询)
  objectStore.createIndex('name', 'name', { unique: false });
};

request.onsuccess = (event) => {
  const db = event.target.result;
  
  // 开始一个读写事务
  const transaction = db.transaction('users', 'readwrite');
  const store = transaction.objectStore('users');
  
  // 添加数据
  store.add({ id: 1, name: 'Alice', age: 25 });
  store.add({ id: 2, name: 'Bob', age: 30 });
  
  // 读取数据
  const getRequest = store.get(1);
  getRequest.onsuccess = (e) => {
    console.log('获取的数据:', e.target.result);
  };
  
  transaction.oncomplete = () => {
    console.log('事务完成');
  };
};

request.onerror = (event) => {
  console.error('数据库打开失败:', event.target.errorCode);
};

4. 使用场景

  • 离线 Web 应用
    可用于存储大量数据,使应用在离线状态下依然可用(如离线文档、离线表单数据等)。

  • 缓存 API 数据
    在构建单页面应用(SPA)时,可将请求的 API 数据缓存到 IndexedDB,减少网络请求、提高性能。

  • 复杂数据管理
    对于需要存储大量复杂结构数据的应用,如图像、视频、文件、游戏数据等,IndexedDB 提供了更灵活和高效的存储方式。

  • 渐进式 Web 应用(PWA)
    PWA 常利用 IndexedDB 存储资源和数据,以支持离线工作和数据持久化。


5. 优点与挑战

优点挑战或注意事项
支持大量数据存储API 较为复杂,学习曲线较陡
异步操作,不会阻塞主线程浏览器之间实现可能存在差异
支持事务和索引,便于高效查询和数据一致性管理错误处理和版本升级需要仔细设计
能够存储丰富的数据类型,如对象、Blob、File 等调试较为困难,需要借助浏览器开发者工具

6. 总结

IndexedDB 是一种功能强大且灵活的客户端数据库,适用于需要存储大量、复杂数据以及支持离线操作的 Web 应用。

  • 如果你的应用需要存储大量数据或复杂结构数据,并且对数据查询性能和一致性有较高要求,IndexedDB 是一个理想的选择。
  • 然而,由于其 API 的复杂性,在使用前需要投入一定时间学习和设计数据模型。

IndexedDB 与其他存储方案(如 localStorage 或 Cookie)相比,容量更大、功能更丰富,但实现和维护上也更具挑战性。选择合适的存储方案时,应根据应用的具体需求、数据敏感性和开发成本进行权衡。

Cookie 是浏览器存储数据的一种机制,它允许服务器在用户设备上存储小型文本数据,并在随后的请求中自动发送这些数据。Cookie 主要用于维护会话状态、用户身份验证、个性化设置和跟踪用户行为。


1. 基本特性

  • 数据存储形式
    Cookie 以键值对形式存储数据,每个 Cookie 通常只有几个 KB 的大小(每个约 4KB 左右)。

  • 自动发送
    浏览器会在每次向服务器发起请求时自动附带相应域的 Cookie 数据,这对于会话管理和身份验证非常有用。

  • 生命周期

    • 会话 Cookie:默认情况下,Cookie 会在浏览器关闭时失效。
    • 持久 Cookie:可以通过设置 ExpiresMax-Age 属性,指定 Cookie 的有效期限,使其在浏览器关闭后仍然有效。
  • 作用域控制
    通过设置 DomainPath 属性,可以控制 Cookie 的可访问范围,确保只有特定路径或子域可以读取或发送该 Cookie。


使用 JavaScript 设置 Cookie:

javascript
// 设置一个持久化 Cookie,有效期至 2025 年 12 月 31 日,适用于整个站点
document.cookie = "username=JohnDoe; path=/; expires=Fri, 31 Dec 2025 12:00:00 GMT";

通过 document.cookie 读取 Cookie,返回值为一个包含所有 Cookie 的字符串:

javascript
const cookies = document.cookie;
console.log(cookies); // 可能输出: "username=JohnDoe; session_id=abc123"

删除 Cookie 的方法通常是设置一个过去的过期时间:

javascript
// 删除名为 "username" 的 Cookie
document.cookie = "username=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT";

3. 安全性设置

为了提高 Cookie 的安全性,可使用以下属性:

  • HttpOnly
    防止 JavaScript 访问 Cookie,减少 XSS 攻击风险。
    示例:Set-Cookie: session_id=abc123; HttpOnly

  • Secure
    限制 Cookie 仅通过 HTTPS 传输,防止在明文传输中被窃取。
    示例:Set-Cookie: session_id=abc123; Secure

  • SameSite
    控制 Cookie 在跨站请求中是否发送,帮助防御 CSRF 攻击。
    可设置为 StrictLaxNone(若设置为 None,则必须同时使用 Secure)。
    示例:Set-Cookie: session_id=abc123; SameSite=Strict


4. 应用场景

  • 会话管理
    存储 Session ID,帮助服务器识别和维护用户会话状态。

  • 身份验证
    用于实现自动登录、记住用户身份。

  • 个性化设置
    保存用户偏好(如语言、主题等)以便在用户下次访问时自动应用。

  • 用户跟踪与分析
    广告商和分析工具可以使用 Cookie 跟踪用户行为,但这也引发了隐私问题。


5. 总结

  • Cookie 是一种轻量级的数据存储机制,因其自动随请求发送的特性,非常适合用于会话管理和身份验证。
  • 通过设置 HttpOnly、Secure、SameSite 等属性,可以大幅提升 Cookie 的安全性。
  • 与其他 Web 存储机制相比,Cookie 在数据容量上较小,但其与服务器的紧密集成使其成为传统 Web 应用中不可或缺的组件。

如果你对 Cookie 的实现细节、最佳实践或与其他存储方案的深度对比感兴趣,我可以进一步展开说明。

Cache Storage

Cache Storage 是由 Service Worker 提供的一种浏览器缓存 API,用于存储 HTTP 请求及其响应数据。它主要用于提升 Web 应用性能和离线体验,能够在应用无网络连接时仍然提供缓存的资源。


1. 主要特性

  • 异步 API
    操作完全异步,不会阻塞主线程,适合存储较大数据和频繁的读写操作。

  • 请求/响应缓存
    专门用于缓存网络请求(如 HTML、CSS、JS、图片等资源)及其响应,开发者可以根据需要决定哪些请求应被缓存。

  • 灵活控制
    开发者可以自定义缓存策略,如缓存优先、网络优先、离线优先等,通过编写 Service Worker 脚本来管理缓存的添加、更新和删除。

  • 持久存储
    缓存数据存储在浏览器中,除非主动删除或缓存失效,否则长期保留,有助于离线访问和快速加载。


2. 使用场景

  • 离线应用支持(PWA)
    缓存网页资源,确保用户在无网络环境下也能正常访问应用的部分功能。

  • 性能优化
    减少重复网络请求,快速加载静态资源,降低延迟,提升用户体验。

  • 动态缓存
    针对用户交互过程中产生的数据,进行缓存和更新,以适应实时性较低的数据展示。


3. 常用 API 与示例

a. 打开/创建缓存

使用 caches.open(cacheName) 方法打开或创建一个命名缓存。

javascript
// 打开或创建一个名为 'my-cache' 的缓存
caches.open('my-cache').then((cache) => {
  // 将指定资源添加到缓存中
  cache.add('/index.html');
});

b. 查找匹配的缓存资源

通过 caches.match(request) 方法可以检索与请求匹配的缓存条目。

javascript
caches.match('/index.html').then((response) => {
  if (response) {
    console.log('从缓存中找到资源', response);
  } else {
    console.log('未找到缓存资源');
  }
});

c. 缓存更新和删除

  • 添加/更新缓存:使用 cache.put(request, response)
  • 删除缓存:使用 caches.delete(cacheName) 删除整个缓存
javascript
// 缓存更新示例
fetch('/new-data.json').then((response) => {
  caches.open('my-cache').then((cache) => {
    cache.put('/new-data.json', response.clone());
  });
});

// 删除指定缓存
caches.delete('my-cache').then((success) => {
  console.log('缓存删除成功:', success);
});

4. 总结

  • Cache Storage 是专为缓存 HTTP 请求和响应而设计的存储机制,主要用于提升页面加载速度和支持离线应用(如 PWA)。
  • 与 localStorage 和 IndexedDB 不同,Cache Storage 主要聚焦于网络资源缓存,通过 Service Worker 管理和更新缓存数据。
  • 适合用来缓存静态资源和部分动态数据,确保在网络状况不佳或离线状态下依然能提供基本功能。

通过合理使用 Cache Storage,开发者可以显著改善 Web 应用的性能和用户体验,尤其在构建渐进式 Web 应用时,它是不可或缺的重要组件。

存储算法

LRU 缓存机制

1. 什么是 LRU 缓存?

LRU(Least Recently Used),即 最近最少使用,是一种 缓存淘汰策略,用于在缓存容量不足时,优先移除最久未使用的数据,从而保留最近频繁访问的数据。


2. LRU 的核心思想

  • 缓存大小固定,当缓存满了,需要删除旧数据。
  • 删除最近最少使用的项(即最久未被访问的数据)
  • 每次访问数据,都会将该数据移动到最前面(最近使用的位置)

3. LRU 适用于哪些场景?

浏览器缓存(如 Vue keep-alive 缓存的组件)
CPU 缓存(提高数据读取效率)
数据库缓存(Redis LRU 淘汰策略)
磁盘缓存(文件系统缓存管理)


4. LRU 实现方式

LRU 常用 哈希表(HashMap)+ 双向链表(Doubly Linked List) 结构来实现:

  1. 哈希表(O(1) 查询) 用于存储缓存数据(键值对)。
  2. 双向链表(O(1) 维护顺序) 记录数据访问顺序:
    • 最近访问的元素放在头部
    • 淘汰最久未使用的元素,即尾部元素

关键操作

  • get(key):访问缓存,如果数据存在,将其移动到链表头部。
  • put(key, value):写入缓存:
    • 如果 缓存未满,直接插入头部。
    • 如果 缓存已满,移除尾部元素,然后插入新数据到头部。

5. LRU 代码实现

使用 JavaScript Map

ES6 Map 可以维护插入顺序,所以可以用 Map 直接实现 LRU:

js
class LRUCache {
  constructor(capacity) {
    this.capacity = capacity;
    this.cache = new Map();
  }

  get(key) {
    if (!this.cache.has(key)) return -1;
    const value = this.cache.get(key);
    this.cache.delete(key);
    this.cache.set(key, value); // 重新插入到 Map 末尾(最近使用)
    return value;
  }

  put(key, value) {
    if (this.cache.has(key)) this.cache.delete(key); // 先删除旧位置
    this.cache.set(key, value);
    if (this.cache.size > this.capacity) {
      // 删除 Map 第一个元素(最久未使用)
      this.cache.delete(this.cache.keys().next().value);
    }
  }
}

// 测试
const cache = new LRUCache(2);
cache.put(1, "A"); // 缓存 = {1=A}
cache.put(2, "B"); // 缓存 = {1=A, 2=B}
console.log(cache.get(1)); // "A"(访问 1,变为最近使用)=> 缓存 = {2=B, 1=A}
cache.put(3, "C"); // 淘汰 2 => 缓存 = {1=A, 3=C}
console.log(cache.get(2)); // -1(2 已被淘汰)

📌 Map 结构按插入顺序存储数据,delete+set 可维护访问顺序
📌 淘汰策略:cache.keys().next().value 获取最早的键,并删除它


使用 HashMap + 双向链表(更高效)

如果需要更高效的实现,我们可以使用:

  • HashMap 存储键值对(O(1) 查询)
  • 双向链表 维护访问顺序(O(1) 插入 & 删除)
js
class LRUCache {
  constructor(capacity) {
    this.capacity = capacity;
    this.map = new Map();
    this.head = {};
    this.tail = {};
    this.head.next = this.tail;
    this.tail.prev = this.head;
  }

  _remove(node) {
    node.prev.next = node.next;
    node.next.prev = node.prev;
  }

  _insert(node) {
    node.next = this.head.next;
    node.prev = this.head;
    this.head.next.prev = node;
    this.head.next = node;
  }

  get(key) {
    if (!this.map.has(key)) return -1;
    const node = this.map.get(key);
    this._remove(node); // 移动到头部
    this._insert(node);
    return node.value;
  }

  put(key, value) {
    if (this.map.has(key)) this._remove(this.map.get(key)); // 删除旧位置

    const newNode = { key, value };
    this._insert(newNode);
    this.map.set(key, newNode);

    if (this.map.size > this.capacity) {
      const lru = this.tail.prev;
      this._remove(lru);
      this.map.delete(lru.key);
    }
  }
}

// 测试
const cache = new LRUCache(2);
cache.put(1, "A"); // {1=A}
cache.put(2, "B"); // {1=A, 2=B}
console.log(cache.get(1)); // "A"(访问后,1 移到最前)=> {2=B, 1=A}
cache.put(3, "C"); // 淘汰 2 => {1=A, 3=C}
console.log(cache.get(2)); // -1(2 已被淘汰)

📌 双向链表 O(1) 维护访问顺序,哈希表 O(1) 查询缓存
📌 尾部淘汰数据,头部插入最近使用的数据


6. keep-alive 中的 LRU 机制

keep-alive 结合 max 选项

vue
<keep-alive :max="3">
  <component :is="currentComponent"></component>
</keep-alive>
  • max=3 最多缓存 3 个组件,超过后会淘汰最久未使用的组件
  • keep-alive 内部使用 LRU 算法来管理缓存组件

** keep-alive 内部如何实现?**

  1. 缓存组件实例(VNode)
    • 维护 cache(哈希表)存储组件。
    • 维护 keys(数组/链表)存储访问顺序。
  2. 访问组件
    • 如果组件已缓存,将其移动到 keys 头部(表示最近访问)。
  3. 超过 max 限制
    • 淘汰 keys 尾部组件,并删除 cache 里对应的实例。

评论区

欢迎留言、补充或勘误。

xiaoba.blog