目录
一、 IndexedDB 介绍
二、 IndexedDB的特点
三、 使用场景
四、在谷歌开发者工具中使用IndexedDB
五、代码实战
5.1 简单增删改查示例
5.2 存储图片和现实图片示例
六、总结

作为一名资深的前端开发者,试想一下这个场景:凌晨1点,咖啡见底,你正在为一个复杂的表单草稿丢失而抓狂。当用户关掉浏览器,表单数据就没了?或者,你雄心勃勃地想做一款离线也能流畅操作的PWA应用,数据存哪?
localStorage
Cookies
没错今天的要分享的主角就是浏览器开发工具中的IndexDB技术,感兴趣的前端朋友可以来了解一下!
一、 IndexedDB 介绍
IndexedDB英文全称为Indexed Database API,简单来说IndexedDB 是浏览器内置的一个事务型、NoSQL 数据库系统。 它让你能在用户的浏览器里持久化存储大量结构化数据(对象、文件/blob等),并且支持高性能的索引查询。目前主流的浏览器都支持IndexedDB,比如:Chrome,Firefox,Opera,Safari完全支持IndexDB,IE10/IE11和Edge部分支持。
核心概念:

数据库 (Database): 一个独立命名的存储容器。每个域名下可以创建多个DB。
对象仓库 (Object Store): 相当于数据库里的“表”,用于存储特定类型的JavaScript对象(键值对集合)。每个对象有一个唯一键。
索引 (Index): 在对象仓库上建立的,用于快速按非主键字段查询数据的结构。
事务 (Transaction): 所有读写操作都必须在事务中进行,保证操作的原子性和一致性(要么全成功,要么全失败)。
游标 (Cursor): 用于遍历对象仓库或索引中的大量数据。
键 (Key): 可以是数字、字符串、日期、数组,甚至是二进制数据。可以是对象本身的属性(内联键),也可以单独指定(外联键)。
二、 IndexedDB的特点

海量存储: 浏览器通常允许单个IndexedDB数据库占用大量磁盘空间。存个几MB、几十MB甚至更大的数据(如图片缓存、文档草稿)完全没问题。
结构化、支持索引: 不像 localStorage 只能存字符串。IndexedDB 存的是 JavaScript 对象。更重要的是,你可以创建索引,实现高效查询(比如按用户名、按时间戳快速查找)。
异步操作: 所有API都是异步的(基于事件或Promise),不会阻塞主线程,保证了页面流畅性。
事务支持: 保证了数据操作的完整性和一致性。尤其在复杂操作(先读A再写B再更新C)时非常关键。
同源策略: 和 localStorage 一样,遵守同源策略。不同域无法访问。
支持二进制数据 (Blob/File): 可以直接存储图片、音频、文件片段等二进制数据,这对于离线图片预览、文档缓存等场景非常有用。
三、 使用场景

离线优先应用 (PWA): 这是 IndexedDB 的主要战场!在用户离线时,将应用数据(用户配置、文章草稿、消息记录、商品列表、图片资源等)完整保存在本地。网络恢复后再同步到服务器。提供无缝的离线体验。
富文本编辑器 / 复杂表单的自动保存: 用户输入内容频繁地、静默地保存到 IndexedDB。即使浏览器崩溃、页面意外关闭,也能恢复大部分内容。比频繁请求服务器保存高效得多。
大型应用数据的客户端缓存: 对于数据量较大、更新频率不高(或增量更新)的数据(如城市列表、商品分类、用户历史记录、配置信息),首次加载后存入 IndexedDB。后续访问优先从本地读取,极大提升加载速度和用户体验,减少服务器压力。
客户端日志/分析数据持久化: 收集的用户行为日志、错误报告等,可以先批量存储在 IndexedDB 中,待网络良好或有足够数量时再统一上报服务器,避免因网络波动导致数据丢失。
文件/资源的本地缓存: 如图片库、文档查看器。用户访问过的图片或文档可以缓存在 IndexedDB 中,下次访问无需下载,实现秒开。
游戏状态保存: 网页游戏的关卡进度、玩家属性、装备信息等,可以方便地保存在 IndexedDB 中。
四、在谷歌开发者工具中使用IndexedDB
这里使用谷歌浏览器开发者工具切换为中文界面来演示
找到它: 打开 谷歌开发者工具 (
F12
Cmd+Opt+I
Ctrl+Shift+I
IndexedDB

查看数据库结构:

点击数据库名展开,能看到它包含的 
Object Stores
点击 
Object Store
注意看 
Key Path
Indexes
查看数据:

在右侧数据列表里,可以直接看到存储的 JavaScript 对象。
右键数据行,可以执行 
Delete

筛选与搜索:

在 
Object Store
Key
Value
对于大型数据集,筛选功能非常实用。
清空与删除:

清空 Object Store: 右键点击某个 
Object Store
Clear object store
删除 Object Store / Index: 右键点击 -> 
Delete
删除整个数据库: 直接在 应用-> 删除数据库 里勾选 
IndexedDB

调试事务与错误:
在 控制台面板中,你的代码操作 IndexedDB 时产生的错误(权限问题、版本冲突、约束错误等)会清晰地打印出来。结合开发者工具中的源代码面板断点调试,定位问题效率极高。
高级)性能分析: 在 性能标签面板录制操作时,可以看到 IndexedDB 读写操作的耗时,帮助优化数据库设计(如索引是否有效)。
使用技巧:
版本升级: 修改数据库结构(增删 Object Store/Index)需要升级 
db.version
onupgradeneeded
异步地狱: 原生 API 是基于事件的回调,写起来容易嵌套。强烈推荐使用封装库:
Dexie.js
idb
存储限制与回收: 浏览器在磁盘空间不足时可能清除 IndexedDB 数据)。重要数据要有备份或同步机制。
DevTools 是上帝视角: 你在 DevTools 里做的删除操作,是“超能力”,不受代码事务限制。线上环境用户可没这能力! 所以你的代码逻辑(增删改查、事务处理、错误捕获)才是王道,开发者工具主要是辅助验证和调试的。
五、代码实战
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">添加数据</button>    <button id="deleteBtn">删除数据</button>    <button id="updateBtn">更新数据</button>    <button id="queryBtn">查询数据</button>    <script>        // 打开或创建数据库        const request = indexedDB.open('myDatabase', 1);        // 数据库打开失败        request.onerror = function (event) {            console.error('数据库打开失败:', event.target.errorCode);        };        // 数据库打开成功        request.onsuccess = function (event) {            const db = event.target.result;            console.log('数据库打开成功');            // 添加数据            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('数据添加成功');                };                addRequest.onerror = function (event) {                    console.error('数据添加失败:', event.target.errorCode);                };            });            // 删除数据            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('数据删除成功');                };                deleteRequest.onerror = function (event) {                    console.error('数据删除失败:', event.target.errorCode);                };            });            // 更新数据            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('数据更新成功');                };                putRequest.onerror = function (event) {                    console.error('数据更新失败:', event.target.errorCode);                };            });            // 查询数据            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('未找到数据');                    }                };                getRequest.onerror = function (event) {                    console.error('数据查询失败:', event.target.errorCode);                };            });        };        // 数据库版本更新时创建对象仓库和索引        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 存储图片和现实图片示例
<!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;        // 状态更新        function updateStatus(message) {            document.getElementById('status').textContent = message;        }        // 1. 初始化数据库(Promise封装)        function initDB() {            return new Promise((resolve, reject) => {                const request = indexedDB.open(DB_NAME, DB_VERSION);                // 数据库结构升级                request.onupgradeneeded = (event) => {                    const db = event.target.result;                    // 关键修复:确保对象存储存在                    if (!db.objectStoreNames.contains(STORE_NAME)) {                        db.createObjectStore(STORE_NAME, {                             keyPath: 'id',                             autoIncrement: true                         });                        updateStatus("对象存储创建成功");                    }                };                request.onsuccess = (event) => {                    db = event.target.result;                    updateStatus("数据库已连接");                    resolve(db);                };                request.onerror = (event) => {                    updateStatus(`数据库错误: ${event.target.error}`);                    reject(event.target.error);                };            });        }        // 2. 存储图片到数据库        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);                // 构建元数据对象                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. 从数据库加载最新图片        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("数据库中没有图片");                }            };            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)})`);        }        // 辅助函数:格式化文件大小        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("正在初始化数据库...");            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它是构建现代、高性能、离线友好型 Web 应用的基石之一。对于前端开发工程师来说属于必备技能。希望本篇文章能对大家了解IndexedDB技术提供一些帮助!
互动时间:
灵魂拷问: 你负责的项目里,哪些数据最适合迁移到 IndexedDB?是用户草稿?配置项?还是缓存的大列表?
踩坑分享: 你在使用 IndexedDB 或者用 DevTools 调试它时,遇到过什么印象深刻的“坑”?说出来让大家避避雷!(比如诡异的版本升级失败?)
第三方库安利: 你更喜欢用哪个 IndexedDB 封装库?
Dexie.js
idb
 
                
 
                 
                 
                





 
                