当前位置: 首页 > news >正文

浏览器缓存之【结构化数据库与缓存】: IndexedDB、Cache storage 和 Storage buckets

前文已经介绍了,基础键值存储 Local storage 和 Session storage,那么本文再介绍另一新的类别:结构化数据库与缓存。

结构化数据库与缓存,涵盖 IndexedDB、Cache storage 和 Storage buckets。

  • IndexedDB 是一个事务型数据库,支持存储大量结构化数据,适合复杂应用如离线文档编辑器或媒体播放器,属于应用数据存储
  • Cache storage 主要用于 Service Worker缓存网络资源,实现离线访问和快速加载,属于资源性能存储
  • Storage buckets 则是更底层的存储分区机制,允许开发者按策略管理存储空间,常用于大型应用的资源隔离与配额控制,属于资源性能存储

下面来详细介绍下。

回到顶部

一、什么是 IndexedDB?

1.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,避免依赖自增 ID(autoIncrement)导致多端同步冲突
分域存储:按业务拆分对象仓库(如:user_settings、chat_messages),避免单表膨胀导致锁竞争。

1.4 基本操作:初始化、增、删、改、查、索引查询

// 【初始化】
const request = indexedDB.open("MyDB", 1); // 名称+版本号
// 首次创建或升级版本时触发
request.onupgradeneeded = (event) => {
const db = event.target.result;
// 创建对象仓库(主键为 id)
const 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)获取匹配该索引值的第一条记录单个对象或undefined
index.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 API),IndexedDB 存储动态数据,实现完整离线体验。

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 storage?

2.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 典型应用场景

1)PWA 离线资源管理

预加载核心资源:在安装阶段缓存 Shell(HTML/CSS/JS),确保 首次加载后完全离线可用。
动态资源缓存:用户访问时缓存图片/数据,后续离线时仍可查看历史内容。

2)自定义缓存策略

忽略查询参数:通过 new Request(url, { ignoreSearch: true }) 匹配忽略 ?v=1.2.3 的资源。
版本化缓存:按版本号命名缓存仓库(如:app-v2),旧版本缓存需手动清理,避免空间浪费。

3)网络优化

降级体验保障:网络请求失败时返回缓存内容(stale-while-revalidate 模式)。
减少重复请求:对静态资源(如:字体、第三方库)实现跨页面/会话的持久缓存

http://www.jsqmd.com/news/1091248/

相关文章:

  • CRMEB电商系统安全审计实战:公开接口漏洞分析与加固方案
  • 3步打造你的专属无线蓝牙控制设备:MicroPython BLE HID终极指南
  • MSP430FR系统控制模块深度解析:JTAG配置、内存保护与安全机制实战
  • 合集 - AI(11)1.本地部署 DeepSeek:小白也能轻松搞定!2025-02-132.如何给本地部署的DeepSeek投喂数据,让他更懂你2025-02-143.本地部署De
  • 禁令两周后,美国政府放宽限制,允许Anthropic向超百家机构提供Mythos 5模型
  • Datasheet 生成 KiCad Symbol
  • 网易云音乐自动打卡神器:每天300首轻松升级LV10的完整实用指南
  • TSW1100高速ADC数据采集卡实战指南:从硬件连接到性能评估
  • 车载系统(IVI)开发入门
  • Jetpack Compose 入门指南
  • Flink 实时数仓开发实战:Catalog 快照,让 DDL 只写一次
  • MSPM0定时器实战:QEI编码器解码与PWM电机控制全解析
  • 吸氢机流量会虚标吗?3个家用检测方法,轻松识破行业猫腻
  • OpenCode 个人习惯设置大全
  • OBS-ASIO插件终极指南:实现专业音频设备的低延迟录制与直播
  • 宏与函数的本质区别(理解场景的前提)
  • 深入解析EASY-HWID-SPOOFER:内核级硬件信息修改技术实现
  • CompressO:免费开源跨平台媒体压缩工具终极指南
  • GD32F303串口驱动开发:从寄存器到中断与环形缓冲区的实战解析
  • 如何3分钟快速安装TrollStore:TrollInstallerX全面指南
  • 创维E900V22C电视盒子刷机指南:三步变身专业4K媒体播放器
  • 客户细分化技术中的聚类分析分类模型与细分策略
  • 3分钟快速上手:用Barrier实现一套键鼠控制多台电脑的终极方案
  • 2026博尔塔拉黄金回收白银回收铂金回收旧料回收怎么选?五家高实价铂金白银线下门店测评清单 + 联系方式
  • Redis 内存分配器调优方案
  • PySpark实战:从数据清洗到模型部署的泰坦尼克号幸存者预测完整流程
  • 江协的51单片机的学习
  • STK与MATLAB联动实战:Walker星座建模与参数解析
  • SQLModel零基础教程(二)- 字段高级配置 数据校验,复用Pydantic能力
  • Vivado HLS高层次综合的设计理念