Skip to content

IndexedDB 与 Cache API

二者均为 异步同源隔离 的持久化能力:IndexedDB 适合大量结构化业务数据;Cache Storage(Cache / CacheStorage)专门缓存 Request/Response,常与 Service Worker 组合做离线资源策略。


IndexedDB(NoSQL)

浏览器内置的键值型存储(异步 API),事务、object store、索引与游标可做复杂查询。

  • 异步 API · 同源隔离 · 持久化 · 容量远大于 Web Storage · 可存结构化对象(非仅限字符串)

工作流程

shell
IndexedDB 通过 open 打开数据库;版本变更时触发 onupgradeneeded,在其中创建对象仓库与索引。
打开成功后所有读写必须通过事务,在 object store 上执行增删改查;结束事务后可关闭连接。

# 流程简图
open(库名, 版本)

onupgradeneeded(建表、索引)

onsuccess(得到 db)

db.transaction(表, 模式) → objectStore

add / get / put / delete / cursor

success / error 回调

事务完成,可 close

# 与关系型概念类比(仅帮助理解)
数据库
ObjectStore
keyPath 主键
index 索引
transaction 事务
cursor 遍历

操作示例

js
function actionUser(user) {
  const request = indexedDB.open('myDB', 1);

  request.onupgradeneeded = (e) => {
    const db = e.target.result;
    if (!db.objectStoreNames.contains('user')) {
      const store = db.createObjectStore('user', { keyPath: 'id' });
      store.createIndex('nameIdx', 'name', { unique: false });
      store.createIndex('ageIdx', 'age', { unique: false });
    }
  };

  request.onerror = (e) => {
    console.log('打开失败', e.target.error);
  };

  request.onsuccess = (e) => {
    const db = e.target.result;
    const tx = db.transaction('user', 'readwrite');
    const store = tx.objectStore('user');

    const addReq = store.add(user);
    addReq.onsuccess = () => console.log('添加成功');
    addReq.onerror = () => console.log('添加失败');

    const getReq = store.get(id);
    getReq.onsuccess = (e) => {
      console.log('查询结果:', e.target.result);
    };

    const reqAll = store.getAll();
    reqAll.onsuccess = (e) => {
      console.log('所有用户:', e.target.result);
    };

    const index = store.index('nameIdx');
    const reqName = index.get(name);
    reqName.onsuccess = (e) => {
      console.log('按姓名查询:', e.target.result);
    };

    store.put(user);
    store.delete(id);

    tx.oncomplete = () => db.close();
  };
}

Cache API(Cache Storage)

CacheStorage(全局 caches)与 Cache 可在 窗口Service Worker 中使用。典型是在 SW 的 fetchmatch / put 做缓存优先、网络回退等。页面线程也可 caches.open 做预缓存,但需与 SW 安装/激活与「按版本清理」策略一致,避免旧缓存长期残留。

  • 只能存成对的 Request / Response(多由 fetch 得到)
  • 全程 Promise,持久化、同源
  • 一般与 Service Worker 搭配,而不是当「通用键值库」

流程与常见方法

shell
caches.open(缓存名) → 得到 cache 实例
 add / addAll / put 写入
 match / matchAll 查询
 delete caches.delete() 清理
js
caches.open('my-cache-v1').then((cache) => {
  cache.add('/api/user');
  cache.add('/static/img/logo.png');
});
// cache.add(url) 相当于 fetch 后 cache.put(request, response)

策略片段

js
// 缓存优先
async function getCacheFirst(url) {
  const cache = await caches.open('my-cache-v1');
  const res = await cache.match(url);
  if (res) return res;
  return fetch(url);
}

// 网络优先
async function getNetworkFirst(url) {
  try {
    const res = await fetch(url);
    const cache = await caches.open('my-cache-v1');
    cache.put(url, res.clone());
    return res;
  } catch {
    return caches.match(url);
  }
}

// 先用缓存、后台更新
async function staleWhileRevalidate(url) {
  const cache = await caches.open('my-cache-v1');
  const cachedRes = await cache.match(url);
  fetch(url).then((res) => {
    cache.put(url, res.clone());
  });
  return cachedRes || fetch(url);
}

四个记忆点

text
只存 Request/Response
全部 Promise
持久、同源
与 SW 场景最搭,别和 IndexedDB 混用角色