【JS Web API】IndexedDB 浏览器端的 NoSQL 数据库指南

2026-05-04 14:30:26

目录:

IndexedDB 介绍

有时候,我们需要将数据存储在浏览器端。对于数据量较小的场景,可以存入 localStorage。但它有容量限制,通常上限为 5MB。当数据量较大时,建议使用 IndexedDB,这是浏览器内置的一种 NoSQL 数据库。相比于 localStorage,IndexedDB 具备以下核心优势:

  • 存储容量大:没有严格的上限限制;
  • 异步操作:所有读写操作都是异步的,不会阻塞主线程,保证了页面流畅;
  • 支持索引:可以为数据字段建立索引,大幅提升查询效率;
  • 支持事务:具备事务机制,多条数据操作要么全部成功,要么全部回滚,确保数据一致性;
  • 支持二进制数据:可以存储 Blob、ArrayBuffer 等二进制对象,可以存储文件、图片等。

连接数据库

onupgradeneeded 是 IndexedDB 中唯一允许创建对象仓库(表)和索引的事件监听器,所有数据库结构的初始化与修改都必须在此事件中完成。

// 第一个参数是数据库名称,第二个参数是版本号
const request = indexedDB.open('MyDB', 1);

// 数据库首次创建或版本升级时触发
request.onupgradeneeded = (event) => {
  const db = event.target.result;
  // 创建对象存储(类似表),主键为 id
  db.createObjectStore('users', { keyPath: 'id' });
};

// 数据库创建成功会进入该方法
request.onsuccess = (event) => {
  const db = event.target.result;
  console.log('数据库打开成功');
  // 后续操作...
};

添加数据

function addUser(db) {
  const tx = db.transaction('users', 'readwrite');
  const store = tx.objectStore('users');
  store.add({ id: 1, name: '张三', age: 25 });
}

读取数据

根据过滤条件读取数据。

function getUser(db, id) {
  const tx = db.transaction('users', 'readonly');
  const store = tx.objectStore('users');
  const request = store.get(id);
  request.onsuccess = () => {
    console.log(request.result) // 是个对象字面量
  };
}

读取全部数据。

function getUser(db, id) {
  const tx = db.transaction('users', 'readonly');
  const store = tx.objectStore('users');
  const request = store.getAll();
  request.onsuccess = () => {
    console.log(request.result) // 是个数组
  };
}

更新数据

function updateUser(db) {
  const tx = db.transaction('users', 'readwrite');
  const store = tx.objectStore('users');
  store.put({ id: 1, name: '李四', age: 30 }); // put 会覆盖已有数据
}

删除数据

function deleteUser(db, id) {
  const tx = db.transaction('users', 'readwrite');
  const store = tx.objectStore('users');
  store.delete(id);
}

HTML 中使用 IndexedDB 实现增删改查完整示例

以下代码可直接复制到 HTML 文件中,在浏览器中打开即可测试运行。

打开控制台,依次展开 Application → IndexedDB,即可看到 UserDatabase,再点开就能看到其中的 users 表。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>IndexedDB 增删改查演示</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; line-height: 1.6; }
        .container { max-width: 800px; margin: 0 auto; }
        button { margin: 5px; padding: 8px 16px; cursor: pointer; }
        input { margin: 5px; padding: 6px; }
        table { width: 100%; border-collapse: collapse; margin-top: 20px; }
        th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
        th { background-color: #f4f4f4; }
        .section { margin: 20px 0; padding: 15px; border: 1px solid #ddd; border-radius: 4px; }
        .result { margin-top: 10px; padding: 10px; background: #f9f9f9; border-left: 3px solid #4CAF50; }
    </style>
</head>
<body>
<div class="container">
    <h1>IndexedDB 增删改查演示</h1>

    <!-- 添加数据区域 -->
    <div class="section">
        <h3>添加用户</h3>
        <input type="text" id="addName" placeholder="姓名">
        <input type="number" id="addAge" placeholder="年龄">
        <input type="email" id="addEmail" placeholder="邮箱">
        <button onclick="addUser()">添加</button>
    </div>

    <!-- 查找数据区域 -->
    <div class="section">
        <h3>查询用户</h3>
        <input type="number" id="queryId" placeholder="输入 ID">
        <button onclick="getUserById()">按ID查询</button>
        <button onclick="getAllUsers()">查询全部</button>
    </div>

    <!-- 更新数据区域 -->
    <div class="section">
        <h3>更新用户</h3>
        <input type="number" id="updateId" placeholder="用户ID">
        <input type="text" id="updateName" placeholder="新姓名">
        <input type="number" id="updateAge" placeholder="新年龄">
        <button onclick="updateUser()">更新</button>
    </div>

    <!-- 删除数据区域 -->
    <div class="section">
        <h3>删除用户</h3>
        <input type="number" id="deleteId" placeholder="输入 ID">
        <button onclick="deleteUser()">删除</button>
        <button onclick="clearAll()">清空全部</button>
    </div>

    <!-- 结果显示区域 -->
    <div class="section">
        <h3>数据展示</h3>
        <table id="userTable">
            <thead>
                <tr>
                    <th>ID</th>
                    <th>姓名</th>
                    <th>年龄</th>
                    <th>邮箱</th>
                </tr>
            </thead>
            <tbody id="userList"></tbody>
        </table>
        <div id="result" class="result">操作结果将显示在这里</div>
    </div>
</div>

<script>
    // ==================== 数据库配置 ====================
    const DB_NAME = 'UserDatabase';
    const DB_VERSION = 1;
    const STORE_NAME = 'users';

    let db = null;

    // ==================== 打开/创建数据库 ====================
    function openDatabase() {
        return new Promise((resolve, reject) => {
            const request = indexedDB.open(DB_NAME, DB_VERSION);

            // 数据库创建或版本升级时触发
            request.onupgradeneeded = (event) => {
                const database = event.target.result;
                
                // 创建对象仓库(类似表),主键为 id,自增
                if (!database.objectStoreNames.contains(STORE_NAME)) {
                    const store = database.createObjectStore(STORE_NAME, { 
                        keyPath: 'id', 
                        autoIncrement: true 
                    });
                    
                    // 创建索引,方便按邮箱等字段查询
                    store.createIndex('email', 'email', { unique: true });
                    store.createIndex('name', 'name', { unique: false });
                    store.createIndex('age', 'age', { unique: false });
                }
            };

            request.onsuccess = (event) => {
                db = event.target.result;
                console.log('数据库打开成功');
                resolve(db);
            };

            request.onerror = (event) => {
                console.error('数据库打开失败:', event.target.error);
                reject(event.target.error);
            };
        });
    }

    // ==================== 添加数据 ====================
    function addUser() {
        const name = document.getElementById('addName').value.trim();
        const age = parseInt(document.getElementById('addAge').value);
        const email = document.getElementById('addEmail').value.trim();

        if (!name || !age || !email) {
            showResult('请填写完整的用户信息');
            return;
        }

        const userData = { name, age, email };

        // 使用事务进行写操作
        const transaction = db.transaction([STORE_NAME], 'readwrite');
        const store = transaction.objectStore(STORE_NAME);
        const request = store.add(userData);

        request.onsuccess = () => {
            showResult(`用户添加成功,ID: ${request.result}`);
            clearInputs();
            getAllUsers(); // 刷新展示
        };

        request.onerror = (event) => {
            if (event.target.error.name === 'ConstraintError') {
                showResult('邮箱已存在,请使用其他邮箱');
            } else {
                showResult('添加失败: ' + event.target.error);
            }
        };
    }

    // ==================== 查询数据 ====================
    // 按ID查询
    function getUserById() {
        const id = parseInt(document.getElementById('queryId').value);
        if (!id) {
            showResult('请输入要查询的 ID');
            return;
        }

        const transaction = db.transaction([STORE_NAME], 'readonly');
        const store = transaction.objectStore(STORE_NAME);
        const request = store.get(id);

        request.onsuccess = () => {
            if (request.result) {
                showResult(`查询结果: ${JSON.stringify(request.result, null, 2)}`);
                renderUsers([request.result]);
            } else {
                showResult('未找到该用户');
            }
        };
    }

    // 查询全部用户
    function getAllUsers() {
        const transaction = db.transaction([STORE_NAME], 'readonly');
        const store = transaction.objectStore(STORE_NAME);
        const request = store.getAll();

        request.onsuccess = () => {
            const users = request.result || [];
            renderUsers(users);
            showResult(`共查询到 ${users.length} 条记录`);
        };
    }

    // ==================== 更新数据 ====================
    function updateUser() {
        const id = parseInt(document.getElementById('updateId').value);
        const name = document.getElementById('updateName').value.trim();
        const age = parseInt(document.getElementById('updateAge').value);

        if (!id) {
            showResult('请输入要更新的用户 ID');
            return;
        }

        // 先查询是否存在
        const transaction = db.transaction([STORE_NAME], 'readwrite');
        const store = transaction.objectStore(STORE_NAME);
        const getRequest = store.get(id);

        getRequest.onsuccess = () => {
            const existingUser = getRequest.result;
            if (!existingUser) {
                showResult('未找到要更新的用户');
                return;
            }

            // 修改数据,使用 put 方法更新
            if (name) existingUser.name = name;
            if (age) existingUser.age = age;

            const updateRequest = store.put(existingUser);
            
            updateRequest.onsuccess = () => {
                showResult(`用户 ID:${id} 更新成功`);
                getAllUsers(); // 刷新展示
            };
        };
    }

    // ==================== 删除数据 ====================
    function deleteUser() {
        const id = parseInt(document.getElementById('deleteId').value);
        if (!id) {
            showResult('请输入要删除的 ID');
            return;
        }

        const transaction = db.transaction([STORE_NAME], 'readwrite');
        const store = transaction.objectStore(STORE_NAME);
        const request = store.delete(id);

        request.onsuccess = () => {
            showResult(`用户 ID:${id} 删除成功`);
            getAllUsers(); // 刷新展示
        };
    }

    // 清空全部数据
    function clearAll() {
        if (!confirm('确定要清空所有用户数据吗?')) return;

        const transaction = db.transaction([STORE_NAME], 'readwrite');
        const store = transaction.objectStore(STORE_NAME);
        const request = store.clear();

        request.onsuccess = () => {
            showResult('所有用户数据已清空');
            renderUsers([]);
        };
    }

    // ==================== 界面辅助函数 ====================
    // 渲染用户表格
    function renderUsers(users) {
        const tbody = document.getElementById('userList');
        tbody.innerHTML = '';
        
        users.forEach(user => {
            const tr = document.createElement('tr');
            tr.innerHTML = `
                <td>${user.id}</td>
                <td>${user.name}</td>
                <td>${user.age}</td>
                <td>${user.email}</td>
            `;
            tbody.appendChild(tr);
        });
    }

    // 显示操作结果
    function showResult(message) {
        document.getElementById('result').textContent = message;
    }

    // 清空输入框
    function clearInputs() {
        document.getElementById('addName').value = '';
        document.getElementById('addAge').value = '';
        document.getElementById('addEmail').value = '';
    }

    // ==================== 初始化 ====================
    // 页面加载完成后打开数据库并加载数据
    window.onload = async () => {
        try {
            await openDatabase();
            getAllUsers(); // 加载已有数据
            showResult('数据库初始化成功,可以开始操作');
        } catch (error) {
            showResult('数据库初始化失败: ' + error.message);
        }
    };
</script>
</body>
</html>

返回首页

本文总阅读量  次
皖ICP备17026209号-3
总访问量: 
总访客量: