React画布应用中IndexedDB onupgradeneeded执行过慢求助
问题分析与解决方案
核心问题定位
笔记本创建耗时过长,主要由以下几个关键问题导致:
- 版本号计算逻辑错误
在updateNotebookNames函数中,版本号的计算存在严重逻辑缺陷:
if(version > notebookNames.filter((notebook) => notebook.username === localStorage.getItem("user"))){ version = notebookNames.filter((notebook) => notebook.username === localStorage.getItem("user")) }
notebookNames.filter(...)返回的是数组对象而非数字长度,直接与数字类型的version比较会导致判断完全失效,最终传给createNotebook的version可能是数组,后续version + 1会变成无效字符串(如[object Object]1)。这种错误的版本号会让IndexedDB无法正确识别升级需求,触发异常的阻塞式升级流程,导致长时间等待。
不必要的数据库升级触发
每次创建笔记本都通过强制提升版本号触发onupgradeneeded,但错误的版本号计算可能导致重复触发多次无效升级。而IndexedDB的升级操作是阻塞式的,多次无效升级会大幅拉长等待时间。组件重复调用创建逻辑
useEffect依赖notebookName,每次notebookName变化都会调用updateNotebookNames,如果没有做好重复判断,可能导致多次重复调用createNotebook,进一步加剧数据库操作的阻塞。
具体修复步骤
1. 修复版本号计算逻辑
将版本号计算改为基于当前用户的笔记本数量,确保传入合法的数字版本:
function updateNotebookNames(name) { if (!notebookNames.some((notebook) => notebook.notebookName === name)) { // ... 原有代码 ... // 正确计算当前用户的笔记本数量 const userNotebooks = notebookNames.filter(notebook => notebook.username === localStorage.getItem("user")); const version = userNotebooks.length + 1; createNotebook(name, version) // ... 后续代码 ... } }
2. 优化IndexedDB升级逻辑
先检查数据库当前版本,避免不必要的版本升级:
export function createNotebook(name, targetVersion) { return new Promise((resolve, reject) => { const dbName = localStorage.getItem("user"); // 先打开数据库获取当前版本,判断是否需要升级 const checkRequest = indexedDB.open(dbName); checkRequest.onsuccess = function() { const currentDb = checkRequest.result; const currentVersion = currentDb.version; currentDb.close(); // 目标版本不大于当前版本时,直接结束流程 if (targetVersion <= currentVersion) { resolve(); return; } // 执行必要的升级操作 const upgradeRequest = indexedDB.open(dbName, targetVersion); upgradeRequest.onupgradeneeded = function(event) { const db = event.target.result; if (!db.objectStoreNames.contains(name)) { db.createObjectStore(name, { autoIncrement: true }); console.log("Object store created for notebook:", name); } }; upgradeRequest.onerror = function(event) { reject("Error opening/creating the database: " + event.target.error); }; upgradeRequest.onsuccess = function() { upgradeRequest.result.close(); // 及时关闭连接释放资源 resolve(); }; }; checkRequest.onerror = function(event) { reject("Error checking database version: " + event.target.error); }; }); }
3. 避免组件重复调用创建逻辑
修改useEffect逻辑,添加重复判断,同时将notebookNames改为状态管理:
// 用useState初始化笔记本列表,避免每次渲染重复读取localStorage const [notebookNames, setNotebookNames] = useState(() => { const stored = localStorage.getItem("notebookNames"); return stored ? JSON.parse(stored) : []; }); useEffect(() => { if (notebookName) { // 先检查是否已存在该笔记本,避免重复创建 const exists = notebookNames.some(n => n.notebookName === notebookName && n.username === localStorage.getItem("user")); if (!exists) { updateNotebookNames(notebookName); } } setName(notebookName); }, [notebookName, notebookNames]); // 更新笔记本列表时同步状态和localStorage function updateNotebookNames(name) { const newNotebooks = [...notebookNames, { username: localStorage.getItem("user"), notebookName: name, }]; setNotebookNames(newNotebooks); localStorage.setItem("notebookNames", JSON.stringify(newNotebooks)); setIsLoading(true); const userNotebooks = newNotebooks.filter(notebook => notebook.username === localStorage.getItem("user")); const version = userNotebooks.length + 1; createNotebook(name, version) .then(() => console.log("Notebook created successfully")) .catch(error => console.error("Error creating notebook:", error)) .finally(() => setIsLoading(false)); }
4. 及时关闭数据库连接
在所有数据库操作成功后及时关闭连接,避免占用浏览器资源:
// 在createNotebook的onsuccess回调中添加关闭逻辑 upgradeRequest.onsuccess = function() { upgradeRequest.result.close(); resolve(); };
额外优化建议
- 对创建笔记本的操作添加防抖,避免用户快速操作导致的多次请求。
- 不要在
onupgradeneeded中添加额外耗时逻辑,仅保留对象仓库创建操作。 - 确保所有IndexedDB操作遵循异步最佳实践,避免阻塞主线程。
内容的提问来源于stack exchange,提问作者Batman
相关产品推荐
相关产品推荐

