为什么IndexedDB缓存数据在页面刷新后丢失了?

打工人艺天 阅读 68

大家好,我在用IndexedDB存用户操作记录时遇到怪事,存完数据马上能读到,但页面刷新后就全没了。明明加了transaction持久化参数啊!

具体场景是这样的:todos列表的缓存,我按教程写了个存储函数:


function saveTodos(todos) {
  const request = indexedDB.open('todoDB', 1);
  request.onsuccess = () => {
    const db = request.result;
    const tx = db.transaction(['todos'], 'readwrite');
    const store = tx.objectStore('todos');
    store.put(todos);
    tx.oncomplete = () => db.close();
  };
}

存数据时用的是saveTodos({items: ['buy milk']}),立即读能拿到数据。但刷新页面后查询就空了,检查浏览器应用面板也看不到数据库。试过改版本号、清空缓存、不同浏览器都一样。难道IndexedDB默认不会持久化存储吗?

我来解答 赞 17 收藏
二维码
手机扫码查看
2 条解答
炜曦的笔记
你这个代码里有个致命问题:每次 open 都是在 onsuccess 里新开一个连接,但没等数据库真正初始化完就急着用,尤其在页面刷新这种场景下特别容易出问题。

关键点在于:indexedDB.open('todoDB', 1) 这个操作是异步的,它会触发 onupgradeneeded 回调来创建/升级 object store,但你代码里压根没处理这个回调——也就是说,第一次运行时可能 object store 根本没建起来,数据自然没地方存;页面一刷新,浏览器发现数据库不存在或者结构不匹配,就重置了。

你得把 onupgradeneeded 补上,确保 store 被正确创建。比如:

function saveTodos(todos) {
const request = indexedDB.open('todoDB', 1);

request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains('todos')) {
db.createObjectStore('todos', { keyPath: 'id' });
}
};

request.onsuccess = (event) => {
const db = event.target.result;
const tx = db.transaction(['todos'], 'readwrite');
const store = tx.objectStore('todos');
store.put({ id: 'todos', items: todos.items });
tx.oncomplete = () => db.close();
};

request.onerror = (event) => {
console.error('IndexedDB error:', event.target.error);
};
}


另外注意两点:一是 put 操作需要有主键,我上面加了个 id 字段;二是别手抖把 keyPath 写错,否则写入可能静默失败。还有个小技巧:开发时可以在 onsuccess 里加个 console.log(db.objectStoreNames) 看看 store 是不是真的建好了。

最后别用 db.close(),除非你确定后面不会再用——一般 IndexedDB 连接是全局复用的,关了下次还得重新 open,反而容易丢数据。
点赞 3
2026-02-25 13:04
迷人的庆晨
问题出在数据库版本控制上,IndexedDB 的版本管理机制有点坑人。你现在的代码每次都重新打开了一个数据库,但没有正确处理版本升级的情况,导致数据被清空了。

解决方法是,在 onupgradeneeded 事件里创建对象存储空间(object store),而不是在每次打开时都创建。如果对象存储已经存在,就不要再重复创建了。

修改后的代码如下:

function saveTodos(todos) {
const request = indexedDB.open('todoDB', 1);

// 关键:这里创建对象存储
request.onupgradeneeded = () => {
const db = request.result;
if (!db.objectStoreNames.contains('todos')) {
db.createObjectStore('todos', { keyPath: 'id', autoIncrement: true });
}
};

request.onsuccess = () => {
const db = request.result;
const tx = db.transaction(['todos'], 'readwrite');
const store = tx.objectStore('todos');

// 注意:这里的 todos 应该是一个数组或单个对象
store.put({ items: ['buy milk'] }); // 示例数据

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


另外提醒一下:
1. todos 数据的结构要符合 IndexedDB 的要求,记得转义特殊字符。
2. 如果涉及到敏感数据(比如用户操作记录),IndexedDB 不适合存储,因为它是客户端存储,容易被篡改或读取。可以考虑加密后再存储,或者干脆用后端保存。
3. 测试时别频繁更改数据库版本号,这会导致数据丢失。改动前想清楚逻辑。

这样改完再试试,应该就没问题了。
点赞 4
2026-01-29 09:08