浏览器缓存之【结构化数据库与缓存】: IndexedDB、Cache storage 和 Storage buckets 前文已经介绍了基础键值存储 Local storage 和 Session storage那么本文再介绍另一新的类别结构化数据库与缓存。结构化数据库与缓存涵盖 IndexedDB、Cache storage 和 Storage buckets。IndexedDB 是一个事务型数据库支持存储大量结构化数据适合复杂应用如离线文档编辑器或媒体播放器属于应用数据存储Cache storage 主要用于 Service Worker缓存网络资源实现离线访问和快速加载属于资源性能存储Storage buckets 则是更底层的存储分区机制允许开发者按策略管理存储空间常用于大型应用的资源隔离与配额控制属于资源性能存储。下面来详细介绍下。回到顶部一、什么是 IndexedDB1.1 简介IndexedDB 是浏览器提供的客户端事务型 NoSQL 数据库专为存储大量结构化数据而设计支持异步操作、事务机制和索引查询适用于构建离线优先的 Web 应用如PWA-Progressive Web App渐进式 Web 应用。其核心价值在于突破 localStorage 的容量与性能限制允许直接存储复杂对象、二进制数据Blob并通过索引实现高效查询同时避免阻塞主线程。IndexedDB 是 W3C 推荐的浏览器原生客户端数据库标准作为 WebSQL 被弃用后的主流替代方案。它属于 NoSQL 类型数据库以对象仓库Object Store而非表格形式组织数据数据以键值对形式存储支持 JavaScript 对象、数组、Blob 等复杂类型无需序列化。若需简化开发可使用封装库如Dexie.js、localForage它们提供 Promise API 和自动降级支持大幅降低原生 IndexedDB 的复杂度。实际项目中应优先评估数据规模与查询需求——当数据量 100KB 或需复杂查询时IndexedDB 是比 localStorage 更可靠的选择。与 localStorage/WebSQL 的关键区别对比维度localStorage / WebSQLIndexedDB容量上限通常限制为 5MB且同步操作易阻塞 UI无固定上限一般 ≥250MB可达设备空闲磁盘的 50%~60%数据操作方式仅支持字符串键值对无索引和事务支持通过事务机制保证数据一致性并支持多字段索引加速查询同步性操作是同步的大数据量时会导致界面卡顿完全异步通过事件或 Promise 执行不阻塞主线程1.2 关键特性1核心能力事务支持操作必须在事务中执行支持 readonly 和 readwrite 模式。事务具备原子性——任一操作失败则整个事务回滚确保数据一致性。索引优化查询可为对象仓库的任意字段创建索引如name、updated_at避免全表扫描。时间范围查询需配合 IDBKeyRange.bound() 使用。二进制数据存储直接存储 Blob/File 对象如图片、视频无需转换为 Base64 字符串适用于离线资源缓存。2限制与约束同源策略数据库仅限创建它的同源页面访问无法跨域读写。版本控制机制数据库通过整数版本号管理结构变更。版本号必须为整数如2.1 会被取整为 2升级时需触发 onupgradeneeded 事件迁移数据。隐身模式行为数据仅驻留在内存中关闭窗口后自动清除且配额通常 ≤120MB常规模式可达数 GB。1.3 核心概念架构1核心组件组件说明数据库顶层容器每个数据库有唯一名称和整数版本号。对象仓库类似 NoSQL 的“集合”存储键值对数据。必须指定主键路径keyPath。索引基于字段的快速查询通道可设为唯一unique: true或非唯一。事务操作的原子单元限定作用域如db.transaction([users], readwrite)。2数据组织逻辑主键设计优先使用业务主键如user_id、message_id作为 keyPath避免依赖自增 IDautoIncrement导致多端同步冲突。分域存储按业务拆分对象仓库如user_settings、chat_messages避免单表膨胀导致锁竞争。1.4 基本操作初始化、增、删、改、查、索引查询// 【初始化】const request indexedDB.open(MyDB, 1); // 名称版本号// 首次创建或升级版本时触发request.onupgradeneeded (event) {const db event.target.result;// 创建对象仓库主键为 idconst store db.createObjectStore(users, { keyPath: id });// 为name字段添加索引store.createIndex(name, name, { unique: false });};request.onsuccess (event) {const db event.target.result; // 获取数据库实例};// 关键点版本升级逻辑必须在 onupgradeneeded 中完成否则无法修改结构const tx db.transaction(users, readwrite);const store tx.objectStore(users);// 【添加数据 add】const request store.add({ id: 1, name: Alice, age: 25 }); // 主键需唯一request.onsuccess () console.log(数据添加成功);request.onerror (event) console.error(数据添加失败:, event.target.errorCode);// 【删除数据 delete】const request store.delete(id);request.onsuccess () console.log(数据删除成功);request.onerror (event) console.error(数据删除失败:, event.target.errorCode);// 【修改】const request store.put({ id: 1, name: Alice, age: 26 });request.onsuccess () console.log(数据更新成功);request.onerror (event) console.error(数据更新失败:, event.target.errorCode);// 【简单查询 get】const request store.get(id);request.onsuccess (event) {if (event.target.result) {console.log(查询结果:, event.target.result);} else {console.log(未找到 ID 为, id, 的数据);}};request.onerror (event) console.error(数据查询失败, event.target.errorCode);// 【索引查询】const index store.index(name);// 查询所有 name Alice 的记录index.getAll(Alice).onsuccess (e) console.log(e.target.result);除了 getAll()IndexedDB 的索引对象还提供了其他几种常用的查询方法以应对不同的业务场景方法说明返回值index.get(value)获取匹配该索引值的第一条记录单个对象或undefinedindex.getAll(value)获取匹配该索引值的所有记录数组[]index.getAllKeys(value)获取匹配该索引值的所有主键主键数组[]index.count(value)统计匹配该索引值的记录数量数字Number注意事务上下文在执行 store.get() 或 store.index() 之前请确保当前处于一个有效的 readwrite 或 readonly 事务Transaction中。如果事务已经结束或发生错误调用这些方法会抛出异常。索引必须提前创建store.index(name) 中的 name 必须在数据库初始化onupgradeneeded时通过 objectStore.createIndex(name, name, { unique: false }) 创建过否则会报错。分批提交单次事务写入 500~2000 条避免长时间阻塞。主动让出主线程用 queueMicrotask 或 setTimeout 间隔执行批次。1.5 典型应用场景1离线优先应用PWA离线数据持久化用户操作实时存入 IndexedDB网络恢复后同步至服务器。配合 Service Worker缓存静态资源Cache APIIndexedDB 存储动态数据实现完整离线体验。2大数据量客户端缓存结构化数据缓存商品目录、用户历史记录等 100KB 的数据避免频繁请求服务器。性能敏感场景对比测试显示2 万条商品数据约 4.8MB的读写IndexedDB 比 localStorage 快 3.8~6.3 倍索引查询效率提升 ≥16 倍。3二进制资源管理图片/文档本地缓存存储 Blob 数据实现“秒开”体验如文档查看器、图片库。游戏状态保存关卡进度、玩家属性等需结构化存储的场景。1.6 使用注意事项与最佳实践1工程化设计原则按业务域拆分仓库避免将用户配置、日志、临时数据混存于同一对象仓库。索引精简化仅高频查询字段建索引长文本字段如content不适合索引全文检索应交由专用库如FlexSearch。2容量与清理策略主动监控配额通过 navigator.storage.estimate() 检测用量当 usage/quota 0.8 时触发清理。分级清理机制优先删除带 expiresAt 的临时数据如日志再清理旧快照避免误删核心数据。3隐私与安全敏感数据加密IndexedDB 本身 无内置加密存储密码等信息需先通过 Web Crypto API 加密。隐身模式限制数据仅内存驻留关闭窗口即销毁不可依赖其持久化能力。回到顶部二、什么是 Cache storage2.1 简介Cache Storage 是浏览器提供的专用于缓存网络请求/响应对Request/Response的持久化存储机制由开发者通过 JavaScript 显式控制独立于 HTTP 缓存头主要用于实现离线优先的 Web 应用如PWA。其核心价值在于完全自主管理资源缓存策略如离线访问、网络回退而非依赖服务器设置的缓存规则。Cache Storage将缓存控制权交给开发者使其能精准实现离线场景需求。实际使用时应避免将其视为“自动缓存”工具——所有缓存操作必须显式编码且需严格管理生命周期。对于简单场景可结合 workbox 等库简化策略实现若仅需缓存静态资源优先通过 HTTP 头配置强缓存而非过度依赖 Cache Storage。Cache Storage 是 W3C Service Workers 规范的一部分但 不限于 Service Worker 环境也可在主线程通过 caches 全局对象访问。它专为存储HTTP 请求-响应对设计直接缓存二进制资源如HTML、CSS、JS、图片而非结构化数据后者应使用 IndexedDB。Cache Storage 与 HTTP 缓存的关键区别对比维度HTTP 缓存Cache Storage控制权归属由服务器通过Cache-Control/Expires响应头控制。完全由开发者通过代码管理忽略 HTTP 缓存头。缓存粒度基于 URL 和响应头自动生效。需显式调用 API 添加/匹配资源支持自定义匹配逻辑如忽略查询参数。适用场景适用于常规页面加载加速。专为离线场景设计如PWA 的离线资源预加载。2.2 核心特性与限制1关键能力持久化存储缓存数据跨页面刷新和浏览器会话保留除非开发者主动删除。独立于网络请求可通过 caches.match(request) 手动检查缓存无需实际发起网络请求。灵活匹配策略支持自定义匹配逻辑如忽略 URL 查询参数通过 Cache.match(request, options) 的 options 参数控制。2重要限制仅支持 GET 请求Cache.add()/addAll()/put() 无法缓存 POST 等非安全请求。HTTPS 强制要求生产环境中必须通过 HTTPS 服务本地开发可使用 http://localhost否则 API 调用会抛出 SecurityError。存储空间受配额约束浏览器对单域名缓存总量有上限通常为设备空闲磁盘的 50%~60%需通过 navigator.storage.estimate() 监控用量。2.3 核心 API 与工作流程基础操作方法有方法作用caches.open(cacheName)打开指定名称的缓存仓库若不存在则自动创建。cache.addAll(urls)批量缓存资源列表任一失败则全部回滚。cache.put(request, response)手动存入请求-响应对常用于动态缓存。caches.match(request)跨所有缓存仓库匹配请求返回首个匹配的响应。cache.keys()获取当前缓存仓库中所有请求的列表。典型工作流程Service Worker 场景1安装阶段预缓存资源self.addEventListener(install, (event) {event.waitUntil(caches.open(v1).then(cache cache.addAll([/index.html, /style.css, /app.js])));});// 关键点通过 waitUntil 确保缓存完成前 Service Worker 不进入激活状态2拦截请求并返回缓存self.addEventListener(fetch, (event) {event.respondWith(caches.match(event.request).then(cachedResponse cachedResponse || fetch(event.request) // 无缓存则走网络));});// 扩展策略可实现 cache-first优先缓存、network-first优先网络等混合策略2.4 典型应用场景1PWA 离线资源管理预加载核心资源在安装阶段缓存 ShellHTML/CSS/JS确保 首次加载后完全离线可用。动态资源缓存用户访问时缓存图片/数据后续离线时仍可查看历史内容。2自定义缓存策略忽略查询参数通过 new Request(url, { ignoreSearch: true }) 匹配忽略 ?v1.2.3 的资源。版本化缓存按版本号命名缓存仓库如app-v2旧版本缓存需手动清理避免空间浪费。3网络优化降级体验保障网络请求失败时返回缓存内容stale-while-revalidate 模式。减少重复请求对静态资源如字体、第三方库实现跨页面/会话的持久缓存。