開發(fā)者工具中的 IndexedDB 實戰(zhàn)指南:離線存儲、緩存優(yōu)化,玩轉瀏覽器數(shù)據(jù)庫!
作為一名資深的前端開發(fā)者,試想一下這個場景:凌晨1點,咖啡見底,你正在為一個復雜的表單草稿丟失而抓狂。當用戶關掉瀏覽器,表單數(shù)據(jù)就沒了?或者,你雄心勃勃地想做一款離線也能流暢操作的PWA應用,數(shù)據(jù)存哪?localStorage 5MB不夠用?Cookies 太小還不安全?這時候,瀏覽器角落里那個名字有點唬人的家伙——IndexedDB,是否猶如靈感般出現(xiàn)在你的腦海中!
沒錯今天的要分享的主角就是瀏覽器開發(fā)工具中的IndexDB技術,感興趣的前端朋友可以來了解一下!
一、 IndexedDB 介紹
IndexedDB英文全稱為Indexed Database API,簡單來說IndexedDB 是瀏覽器內置的一個事務型、NoSQL 數(shù)據(jù)庫系統(tǒng)。 它讓你能在用戶的瀏覽器里持久化存儲大量結構化數(shù)據(jù)(對象、文件/blob等),并且支持高性能的索引查詢。目前主流的瀏覽器都支持IndexedDB,比如:Chrome,F(xiàn)irefox,Opera,Safari完全支持IndexDB,IE10/IE11和Edge部分支持。
核心概念:
圖片
數(shù)據(jù)庫 (Database): 一個獨立命名的存儲容器。每個域名下可以創(chuàng)建多個DB。
對象倉庫 (Object Store): 相當于數(shù)據(jù)庫里的“表”,用于存儲特定類型的JavaScript對象(鍵值對集合)。每個對象有一個唯一鍵。
索引 (Index): 在對象倉庫上建立的,用于快速按非主鍵字段查詢數(shù)據(jù)的結構。
事務 (Transaction): 所有讀寫操作都必須在事務中進行,保證操作的原子性和一致性(要么全成功,要么全失?。?。
游標 (Cursor): 用于遍歷對象倉庫或索引中的大量數(shù)據(jù)。
鍵 (Key): 可以是數(shù)字、字符串、日期、數(shù)組,甚至是二進制數(shù)據(jù)??梢允菍ο蟊旧淼膶傩裕▋嚷?lián)鍵),也可以單獨指定(外聯(lián)鍵)。
二、 IndexedDB的特點
圖片
海量存儲: 瀏覽器通常允許單個IndexedDB數(shù)據(jù)庫占用大量磁盤空間。存?zhèn)€幾MB、幾十MB甚至更大的數(shù)據(jù)(如圖片緩存、文檔草稿)完全沒問題。
結構化、支持索引: 不像 localStorage 只能存字符串。IndexedDB 存的是 JavaScript 對象。更重要的是,你可以創(chuàng)建索引,實現(xiàn)高效查詢(比如按用戶名、按時間戳快速查找)。
異步操作: 所有API都是異步的(基于事件或Promise),不會阻塞主線程,保證了頁面流暢性。
事務支持: 保證了數(shù)據(jù)操作的完整性和一致性。尤其在復雜操作(先讀A再寫B(tài)再更新C)時非常關鍵。
同源策略: 和 localStorage 一樣,遵守同源策略。不同域無法訪問。
支持二進制數(shù)據(jù) (Blob/File): 可以直接存儲圖片、音頻、文件片段等二進制數(shù)據(jù),這對于離線圖片預覽、文檔緩存等場景非常有用。
三、 使用場景
圖片
離線優(yōu)先應用 (PWA): 這是 IndexedDB 的主要戰(zhàn)場!在用戶離線時,將應用數(shù)據(jù)(用戶配置、文章草稿、消息記錄、商品列表、圖片資源等)完整保存在本地。網(wǎng)絡恢復后再同步到服務器。提供無縫的離線體驗。
富文本編輯器 / 復雜表單的自動保存: 用戶輸入內容頻繁地、靜默地保存到 IndexedDB。即使瀏覽器崩潰、頁面意外關閉,也能恢復大部分內容。比頻繁請求服務器保存高效得多。
大型應用數(shù)據(jù)的客戶端緩存: 對于數(shù)據(jù)量較大、更新頻率不高(或增量更新)的數(shù)據(jù)(如城市列表、商品分類、用戶歷史記錄、配置信息),首次加載后存入 IndexedDB。后續(xù)訪問優(yōu)先從本地讀取,極大提升加載速度和用戶體驗,減少服務器壓力。
客戶端日志/分析數(shù)據(jù)持久化: 收集的用戶行為日志、錯誤報告等,可以先批量存儲在 IndexedDB 中,待網(wǎng)絡良好或有足夠數(shù)量時再統(tǒng)一上報服務器,避免因網(wǎng)絡波動導致數(shù)據(jù)丟失。
文件/資源的本地緩存: 如圖片庫、文檔查看器。用戶訪問過的圖片或文檔可以緩存在 IndexedDB 中,下次訪問無需下載,實現(xiàn)秒開。
游戲狀態(tài)保存: 網(wǎng)頁游戲的關卡進度、玩家屬性、裝備信息等,可以方便地保存在 IndexedDB 中。
四、在谷歌開發(fā)者工具中使用IndexedDB
這里使用谷歌瀏覽器開發(fā)者工具切換為中文界面來演示
找到它: 打開 谷歌開發(fā)者工具 (F12 / Cmd+Opt+I / Ctrl+Shift+I) -> 切換到 應用 面板 -> 左側菜單找到 存儲 下的 IndexedDB。你會看到當前頁面域名下的所有 IndexedDB 數(shù)據(jù)庫。
圖片
查看數(shù)據(jù)庫結構:
圖片
點擊數(shù)據(jù)庫名展開,能看到它包含的 Object Stores。
點擊 Object Store 名字,右側面板會展示其存儲的數(shù)據(jù)列表(鍵值對)。
注意看 Key Path 和 Indexes,這決定了數(shù)據(jù)的組織和查詢方式。
查看數(shù)據(jù):
圖片
在右側數(shù)據(jù)列表里,可以直接看到存儲的 JavaScript 對象。
右鍵數(shù)據(jù)行,可以執(zhí)行 Delete 操作。
圖片
篩選與搜索:
圖片
在 Object Store 數(shù)據(jù)視圖的頂部,有篩選輸入框??梢愿鶕?jù)鍵 (Key) 或值 (Value) 進行過濾(支持部分匹配)。
對于大型數(shù)據(jù)集,篩選功能非常實用。
清空與刪除:
圖片
清空 Object Store: 右鍵點擊某個 Object Store -> Clear object store。瞬間清空這個“表”的所有數(shù)據(jù)。
刪除 Object Store / Index: 右鍵點擊 -> Delete。注意:這會刪除結構定義和數(shù)據(jù)!
刪除整個數(shù)據(jù)庫: 直接在 應用-> 刪除數(shù)據(jù)庫 里勾選 IndexedDB 進行清除。
圖片
調試事務與錯誤:
在 控制臺面板中,你的代碼操作 IndexedDB 時產生的錯誤(權限問題、版本沖突、約束錯誤等)會清晰地打印出來。結合開發(fā)者工具中的源代碼面板斷點調試,定位問題效率極高。
高級)性能分析: 在 性能標簽面板錄制操作時,可以看到 IndexedDB 讀寫操作的耗時,幫助優(yōu)化數(shù)據(jù)庫設計(如索引是否有效)。
使用技巧:
版本升級: 修改數(shù)據(jù)庫結構(增刪 Object Store/Index)需要升級 db.version。DevTools 里能看到當前版本號。升級邏輯要在 onupgradeneeded 事件里寫。務必在DevTools里測試好升級邏輯! 否則線上用戶數(shù)據(jù)可能出問題。
異步地獄: 原生 API 是基于事件的回調,寫起來容易嵌套。強烈推薦使用封裝庫:Dexie.js, idb (Jake Archibald 的輕量封裝) 等。它們提供 Promise API,代碼清爽幾十倍!在 DevTools 里調試時,這些庫操作的數(shù)據(jù)同樣可見。
存儲限制與回收: 瀏覽器在磁盤空間不足時可能清除 IndexedDB 數(shù)據(jù))。重要數(shù)據(jù)要有備份或同步機制。
DevTools 是上帝視角: 你在 DevTools 里做的刪除操作,是“超能力”,不受代碼事務限制。線上環(huán)境用戶可沒這能力! 所以你的代碼邏輯(增刪改查、事務處理、錯誤捕獲)才是王道,開發(fā)者工具主要是輔助驗證和調試的。
五、代碼實戰(zhàn)
5.1 簡單增刪改查示例
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>IndexedDB 增刪改查示例</title></head><body> <button id="addBtn">添加數(shù)據(jù)</button> <button id="deleteBtn">刪除數(shù)據(jù)</button> <button id="updateBtn">更新數(shù)據(jù)</button> <button id="queryBtn">查詢數(shù)據(jù)</button> <script> // 打開或創(chuàng)建數(shù)據(jù)庫 const request = indexedDB.open('myDatabase', 1); // 數(shù)據(jù)庫打開失敗 request.onerror = function (event) { console.error('數(shù)據(jù)庫打開失敗:', event.target.errorCode); }; // 數(shù)據(jù)庫打開成功 request.onsuccess = function (event) { const db = event.target.result; console.log('數(shù)據(jù)庫打開成功'); // 添加數(shù)據(jù) document.getElementById('addBtn').addEventListener('click', function () { const transaction = db.transaction(['myObjectStore'], 'readwrite'); const objectStore = transaction.objectStore('myObjectStore'); const data = { id: 1, name: '李強', age: 30 }; const addRequest = objectStore.add(data); addRequest.onsuccess = function () { console.log('數(shù)據(jù)添加成功'); }; addRequest.onerror = function (event) { console.error('數(shù)據(jù)添加失敗:', event.target.errorCode); }; }); // 刪除數(shù)據(jù) document.getElementById('deleteBtn').addEventListener('click', function () { const transaction = db.transaction(['myObjectStore'], 'readwrite'); const objectStore = transaction.objectStore('myObjectStore'); const deleteRequest = objectStore.delete(1); deleteRequest.onsuccess = function () { console.log('數(shù)據(jù)刪除成功'); }; deleteRequest.onerror = function (event) { console.error('數(shù)據(jù)刪除失敗:', event.target.errorCode); }; }); // 更新數(shù)據(jù) document.getElementById('updateBtn').addEventListener('click', function () { const transaction = db.transaction(['myObjectStore'], 'readwrite'); const objectStore = transaction.objectStore('myObjectStore'); const data = { id: 1, name: 'Jane', age: 30 }; const putRequest = objectStore.put(data); putRequest.onsuccess = function () { console.log('數(shù)據(jù)更新成功'); }; putRequest.onerror = function (event) { console.error('數(shù)據(jù)更新失敗:', event.target.errorCode); }; }); // 查詢數(shù)據(jù) document.getElementById('queryBtn').addEventListener('click', function () { const transaction = db.transaction(['myObjectStore'], 'readonly'); const objectStore = transaction.objectStore('myObjectStore'); const getRequest = objectStore.get(1); getRequest.onsuccess = function (event) { const result = event.target.result; if (result) { console.log('查詢結果:', result); } else { console.log('未找到數(shù)據(jù)'); } }; getRequest.onerror = function (event) { console.error('數(shù)據(jù)查詢失敗:', event.target.errorCode); }; }); }; // 數(shù)據(jù)庫版本更新時創(chuàng)建對象倉庫和索引 request.onupgradeneeded = function (event) { const db = event.target.result; if (!db.objectStoreNames.contains('myObjectStore')) { const objectStore = db.createObjectStore('myObjectStore', { keyPath: 'id' }); objectStore.createIndex('name', 'name', { unique: false }); objectStore.createIndex('age', 'age', { unique: false }); } }; </script></body></html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>IndexedDB 增刪改查示例</title>
</head>
<body>
<button id="addBtn">添加數(shù)據(jù)</button>
<button id="deleteBtn">刪除數(shù)據(jù)</button>
<button id="updateBtn">更新數(shù)據(jù)</button>
<button id="queryBtn">查詢數(shù)據(jù)</button>
<script>
// 打開或創(chuàng)建數(shù)據(jù)庫
const request = indexedDB.open('myDatabase', 1);
// 數(shù)據(jù)庫打開失敗
request.onerror = function (event) {
console.error('數(shù)據(jù)庫打開失敗:', event.target.errorCode);
};
// 數(shù)據(jù)庫打開成功
request.onsuccess = function (event) {
const db = event.target.result;
console.log('數(shù)據(jù)庫打開成功');
// 添加數(shù)據(jù)
document.getElementById('addBtn').addEventListener('click', function () {
const transaction = db.transaction(['myObjectStore'], 'readwrite');
const objectStore = transaction.objectStore('myObjectStore');
const data = { id: 1, name: '李強', age: 30 };
const addRequest = objectStore.add(data);
addRequest.onsuccess = function () {
console.log('數(shù)據(jù)添加成功');
};
addRequest.onerror = function (event) {
console.error('數(shù)據(jù)添加失敗:', event.target.errorCode);
};
});
// 刪除數(shù)據(jù)
document.getElementById('deleteBtn').addEventListener('click', function () {
const transaction = db.transaction(['myObjectStore'], 'readwrite');
const objectStore = transaction.objectStore('myObjectStore');
const deleteRequest = objectStore.delete(1);
deleteRequest.onsuccess = function () {
console.log('數(shù)據(jù)刪除成功');
};
deleteRequest.onerror = function (event) {
console.error('數(shù)據(jù)刪除失敗:', event.target.errorCode);
};
});
// 更新數(shù)據(jù)
document.getElementById('updateBtn').addEventListener('click', function () {
const transaction = db.transaction(['myObjectStore'], 'readwrite');
const objectStore = transaction.objectStore('myObjectStore');
const data = { id: 1, name: 'Jane', age: 30 };
const putRequest = objectStore.put(data);
putRequest.onsuccess = function () {
console.log('數(shù)據(jù)更新成功');
};
putRequest.onerror = function (event) {
console.error('數(shù)據(jù)更新失敗:', event.target.errorCode);
};
});
// 查詢數(shù)據(jù)
document.getElementById('queryBtn').addEventListener('click', function () {
const transaction = db.transaction(['myObjectStore'], 'readonly');
const objectStore = transaction.objectStore('myObjectStore');
const getRequest = objectStore.get(1);
getRequest.onsuccess = function (event) {
const result = event.target.result;
if (result) {
console.log('查詢結果:', result);
} else {
console.log('未找到數(shù)據(jù)');
}
};
getRequest.onerror = function (event) {
console.error('數(shù)據(jù)查詢失敗:', event.target.errorCode);
};
});
};
// 數(shù)據(jù)庫版本更新時創(chuàng)建對象倉庫和索引
request.onupgradeneeded = function (event) {
const db = event.target.result;
if (!db.objectStoreNames.contains('myObjectStore')) {
const objectStore = db.createObjectStore('myObjectStore', { keyPath: 'id' });
objectStore.createIndex('name', 'name', { unique: false });
objectStore.createIndex('age', 'age', { unique: false });
}
};
</script>
</body>
</html>
圖片
5.2 存儲圖片和現(xiàn)實圖片示例
<!DOCTYPE html><html lang="zh-CN"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>IndexedDB 圖片存儲(原生JS)</title> <style> .preview { border: 1px solid #ddd; min-height: 300px; margin: 20px 0; } button { padding: 8px 12px; margin-right: 10px; } </style></head><body> <input type="file" id="fileInput" accept="image/*"> <button id="storeBtn">存儲圖片</button> <button id="loadBtn">加載最新圖片</button> <div class="preview"> <img id="dbImage" style="max-width: 100%; display: none;"> </div> <div id="status">就緒</div> <script> // 核心配置 const DB_NAME = "ImageDB"; const STORE_NAME = "images"; const DB_VERSION = 1; let db = null; // 狀態(tài)更新 function updateStatus(message) { document.getElementById('status').textContent = message; } // 1. 初始化數(shù)據(jù)庫(Promise封裝) function initDB() { return new Promise((resolve, reject) => { const request = indexedDB.open(DB_NAME, DB_VERSION); // 數(shù)據(jù)庫結構升級 request.onupgradeneeded = (event) => { const db = event.target.result; // 關鍵修復:確保對象存儲存在 if (!db.objectStoreNames.contains(STORE_NAME)) { db.createObjectStore(STORE_NAME, { keyPath: 'id', autoIncrement: true }); updateStatus("對象存儲創(chuàng)建成功"); } }; request.onsuccess = (event) => { db = event.target.result; updateStatus("數(shù)據(jù)庫已連接"); resolve(db); }; request.onerror = (event) => { updateStatus(`數(shù)據(jù)庫錯誤: ${event.target.error}`); reject(event.target.error); }; }); } // 2. 存儲圖片到數(shù)據(jù)庫 async function storeImage() { const file = document.getElementById('fileInput').files[0]; if (!file) { updateStatus("請選擇圖片文件"); return; } try { // 讀取為ArrayBuffer(兼容性更好) const arrayBuffer = await new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => resolve(reader.result); reader.onerror = () => reject(reader.error); reader.readAsArrayBuffer(file); }); const transaction = db.transaction([STORE_NAME], 'readwrite'); const store = transaction.objectStore(STORE_NAME); // 構建元數(shù)據(jù)對象 const imageData = { name: file.name, type: file.type, size: file.size, timestamp: new Date(), imageData: arrayBuffer }; // 存儲操作 const request = store.add(imageData); request.onsuccess = () => { updateStatus(`圖片存儲成功 ID: ${request.result}`); }; request.onerror = (event) => { updateStatus(`存儲失敗: ${event.target.error}`); }; } catch (error) { updateStatus(`錯誤: ${error.message}`); } } // 3. 從數(shù)據(jù)庫加載最新圖片 async function loadLatestImage() { const transaction = db.transaction([STORE_NAME], 'readonly'); const store = transaction.objectStore(STORE_NAME); const request = store.openCursor(null, 'prev'); // 反向遍歷取最新 request.onsuccess = (event) => { const cursor = event.target.result; if (cursor) { displayImage(cursor.value); } else { updateStatus("數(shù)據(jù)庫中沒有圖片"); } }; request.onerror = (event) => { updateStatus(`加載失敗: ${event.target.error}`); }; } // 4. 顯示圖片 function displayImage(imageRecord) { const blob = new Blob([imageRecord.imageData], { type: imageRecord.type }); const imageUrl = URL.createObjectURL(blob); const imgElement = document.getElementById('dbImage'); imgElement.src = imageUrl; imgElement.style.display = 'block'; imgElement.alt = `已加載: ${imageRecord.name}`; // 釋放內存 imgElement.onload = () => URL.revokeObjectURL(imageUrl); updateStatus(`已加載: ${imageRecord.name} (${formatBytes(imageRecord.size)})`); } // 輔助函數(shù):格式化文件大小 function formatBytes(bytes) { const units = ['B', 'KB', 'MB', 'GB']; let size = bytes; let unitIndex = 0; while (size >= 1024 && unitIndex < units.length - 1) { size /= 1024; unitIndex++; } return `${size.toFixed(2)} ${units[unitIndex]}`; } // 5. 初始化應用 window.addEventListener('DOMContentLoaded', async () => { updateStatus("正在初始化數(shù)據(jù)庫..."); try { await initDB(); // 綁定事件 document.getElementById('storeBtn').addEventListener('click', storeImage); document.getElementById('loadBtn').addEventListener('click', loadLatestImage); updateStatus("就緒:選擇圖片后點擊存儲"); } catch (error) { updateStatus(`初始化失敗: ${error.message}`); } }); </script></body></html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>IndexedDB 圖片存儲(原生JS)</title>
<style>
.preview { border: 1px solid #ddd; min-height: 300px; margin: 20px 0; }
button { padding: 8px 12px; margin-right: 10px; }
</style>
</head>
<body>
<input type="file" id="fileInput" accept="image/*">
<button id="storeBtn">存儲圖片</button>
<button id="loadBtn">加載最新圖片</button>
<div class="preview">
<img id="dbImage" style="max-width: 100%; display: none;">
</div>
<div id="status">就緒</div>
<script>
// 核心配置
const DB_NAME = "ImageDB";
const STORE_NAME = "images";
const DB_VERSION = 1;
let db = null;
// 狀態(tài)更新
function updateStatus(message) {
document.getElementById('status').textContent = message;
}
// 1. 初始化數(shù)據(jù)庫(Promise封裝)
function initDB() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(DB_NAME, DB_VERSION);
// 數(shù)據(jù)庫結構升級
request.onupgradeneeded = (event) => {
const db = event.target.result;
// 關鍵修復:確保對象存儲存在
if (!db.objectStoreNames.contains(STORE_NAME)) {
db.createObjectStore(STORE_NAME, {
keyPath: 'id',
autoIncrement: true
});
updateStatus("對象存儲創(chuàng)建成功");
}
};
request.onsuccess = (event) => {
db = event.target.result;
updateStatus("數(shù)據(jù)庫已連接");
resolve(db);
};
request.onerror = (event) => {
updateStatus(`數(shù)據(jù)庫錯誤: ${event.target.error}`);
reject(event.target.error);
};
});
}
// 2. 存儲圖片到數(shù)據(jù)庫
async function storeImage() {
const file = document.getElementById('fileInput').files[0];
if (!file) {
updateStatus("請選擇圖片文件");
return;
}
try {
// 讀取為ArrayBuffer(兼容性更好)
const arrayBuffer = await new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = () => reject(reader.error);
reader.readAsArrayBuffer(file);
});
const transaction = db.transaction([STORE_NAME], 'readwrite');
const store = transaction.objectStore(STORE_NAME);
// 構建元數(shù)據(jù)對象
const imageData = {
name: file.name,
type: file.type,
size: file.size,
timestamp: new Date(),
imageData: arrayBuffer
};
// 存儲操作
const request = store.add(imageData);
request.onsuccess = () => {
updateStatus(`圖片存儲成功 ID: ${request.result}`);
};
request.onerror = (event) => {
updateStatus(`存儲失敗: ${event.target.error}`);
};
} catch (error) {
updateStatus(`錯誤: ${error.message}`);
}
}
// 3. 從數(shù)據(jù)庫加載最新圖片
async function loadLatestImage() {
const transaction = db.transaction([STORE_NAME], 'readonly');
const store = transaction.objectStore(STORE_NAME);
const request = store.openCursor(null, 'prev'); // 反向遍歷取最新
request.onsuccess = (event) => {
const cursor = event.target.result;
if (cursor) {
displayImage(cursor.value);
} else {
updateStatus("數(shù)據(jù)庫中沒有圖片");
}
};
request.onerror = (event) => {
updateStatus(`加載失敗: ${event.target.error}`);
};
}
// 4. 顯示圖片
function displayImage(imageRecord) {
const blob = new Blob([imageRecord.imageData], { type: imageRecord.type });
const imageUrl = URL.createObjectURL(blob);
const imgElement = document.getElementById('dbImage');
imgElement.src = imageUrl;
imgElement.style.display = 'block';
imgElement.alt = `已加載: ${imageRecord.name}`;
// 釋放內存
imgElement.onload = () => URL.revokeObjectURL(imageUrl);
updateStatus(`已加載: ${imageRecord.name} (${formatBytes(imageRecord.size)})`);
}
// 輔助函數(shù):格式化文件大小
function formatBytes(bytes) {
const units = ['B', 'KB', 'MB', 'GB'];
let size = bytes;
let unitIndex = 0;
while (size >= 1024 && unitIndex < units.length - 1) {
size /= 1024;
unitIndex++;
}
return `${size.toFixed(2)} ${units[unitIndex]}`;
}
// 5. 初始化應用
window.addEventListener('DOMContentLoaded', async () => {
updateStatus("正在初始化數(shù)據(jù)庫...");
try {
await initDB();
// 綁定事件
document.getElementById('storeBtn').addEventListener('click', storeImage);
document.getElementById('loadBtn').addEventListener('click', loadLatestImage);
updateStatus("就緒:選擇圖片后點擊存儲");
} catch (error) {
updateStatus(`初始化失敗: ${error.message}`);
}
});
</script>
</body>
</html>查看存儲效果
加載圖片效果
圖片
六、總結
IndexedDB它是構建現(xiàn)代、高性能、離線友好型 Web 應用的基石之一。對于前端開發(fā)工程師來說屬于必備技能。希望本篇文章能對大家了解IndexedDB技術提供一些幫助!
互動時間:
靈魂拷問: 你負責的項目里,哪些數(shù)據(jù)最適合遷移到 IndexedDB?是用戶草稿?配置項?還是緩存的大列表?
踩坑分享: 你在使用 IndexedDB 或者用 DevTools 調試它時,遇到過什么印象深刻的“坑”?說出來讓大家避避雷?。ū热缭幃惖陌姹旧壥??)
第三方庫安利: 你更喜歡用哪個 IndexedDB 封裝庫?Dexie.js?idb?還是其他?為啥?






























