惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 司徒正美
M
MIT News - Artificial intelligence
博客园_首页
IT之家
IT之家
L
LangChain Blog
D
DataBreaches.Net
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Google DeepMind News
Google DeepMind News
Blog — PlanetScale
Blog — PlanetScale
人人都是产品经理
人人都是产品经理
博客园 - Franky
云风的 BLOG
云风的 BLOG
罗磊的独立博客
量子位
G
Google Developers Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园 - 【当耐特】
博客园 - 叶小钗
S
SegmentFault 最新的问题
Stack Overflow Blog
Stack Overflow Blog
B
Blog
T
Tailwind CSS Blog
A
About on SuperTechFans
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com

博客园 - 前端路引

高精度计算插件 decimal.js 处理 JS 浮点数精度问题(0.1 + 0.2 !== 0.3) 还在痛苦前端的拖拽排序吗?SortableJS 轻松搞定 浏览器 15 个常见指纹特征,使用插件 FingerprintJS 生成浏览器指纹 Web前端入门第 90 问:JavaScript 也能无中生有的创建音频 Web前端入门第 89 问:总结 8 种跨域通信处理方案 Web前端入门第 88 问:引入 JavaScript 的 script 标签究竟有多少用法? Web前端入门第 87 问:JavaScript 中 setInterval 和 setTimeout 细节 Web前端入门第 86 问:JavaScript 中的 Web Worker 为什么能提升代码性能? Web前端入门第 84 问:JavaScript sessionStorage 那些容易踩坑的地方 Web前端入门第 83 问:JavaScript localStorage 有大小限制吗?溢出会怎样? Web前端入门第 82 问:JavaScript cookie 有大小限制吗?溢出会怎样? Web前端入门第 81 问:JavaScript cookie 的读写操作 Web前端入门第 80 问:JavaScript 哪些地方需要 try...catch 异常捕获 Web前端入门第 79 问:JavaScript async & await 的异步任务进化之路 Web前端入门第 78 问:JavaScript 比较少见的模版字符串用法 Web前端入门第 77 问:JavaScript 由程序触发绑定事件的几种方式 Web前端入门第 76 问:JavaScript 鼠标事件(mouse) enter/leave 和 over/out 区别 Web前端入门第 75 问:JavaScript 性能优化之事件委托(事件代理)原理 Web前端入门第 74 问:JavaScript 事件冒泡与事件捕获
Web前端入门第 85 问:JavaScript 一个简单的 IndexedDB 数据...
前端路引 · 2025-08-28 · via 博客园 - 前端路引

在前端风风雨雨的混了多年,从没在项目中实际使用过 IndexedDB 这个浏览器端的数据库,所以今天就摸了下 MDN 的后门,写一个简单的入门示例。

页面大概长这样:

85-1

源码:

以下代码包含了一个数据库所有的 CRUD (增删改查)操作。

<div>
  <button id="js_add_btn">添加书籍</button>
</div>
<div>
  <input type="text" name="" id="js_book_id" placeholder="输入书籍ID"/>
  <button id="js_get_by_id_btn">查询书籍</button>
  <button id="js_update_by_id_btn">更新书籍</button>
  <button id="js_delete_by_id_btn">删除书籍</button>
</div>
<div>
  <input type="text" name="" id="js_book_author" placeholder="输入书籍作者查询"/><button id="js_get_by_author_btn">查询书籍</button>
</div>
<div>
  <button id="js_get_all_btn">查询所有书籍</button>
</div>
<div id="output"></div>

<script>
  (() => {
    // 数据库配置
    const dbName = 'MyDB';
    const storeName = 'books';
    const output = document.getElementById('output')
    let db = null;

    function setOutputContent(html) {
      output.innerText = html;
    }

    // 初始化数据库
    const initDB = () => {
      const request = indexedDB.open(dbName);

      // 数据库升级/创建时触发
      request.onupgradeneeded = (event) => {
        db = event.target.result;

        // 创建对象存储空间(类似数据库表)
        const store = db.createObjectStore(storeName, {
          keyPath: 'id', // 主键
          autoIncrement: true, // 自动生成ID
        });

        // 创建索引(允许通过作者查询)
        store.createIndex('author_idx', 'author', { unique: false });

        console.log('数据库已创建/升级');
      };

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

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

    // 封装包装函数,用于执行数据库相关方法
    function wrapper(func) {
      return new Promise((resolve, reject) => {
        const transaction = db.transaction([storeName], 'readwrite');
        const store = transaction.objectStore(storeName);
        const res = func(store)
        res.onsuccess = () => {
          resolve(res.result)
        };

        res.onerror = (event) => {
          reject(event.target.error)
        };
      });
    }

    // 添加数据
    const addBook = async (book) => {
      try {
        const result = await wrapper((store) => store.add(book))
        console.log(result);
        setOutputContent(`添加成功!书籍ID: ${result}`);
      } catch (error) {
        console.error(error);
        setOutputContent('添加失败');
      }
    };

    // 通过ID查询数据
    const getBook = async (id) => {
      try {
        const book = await wrapper((store) => store.get(id))
        if (book) {
          console.log('查询结果:', book);
          setOutputContent(`书名: ${book.title}\n作者: ${book.author}\n价格: $${book.price}`);
        } else {
          console.log('未找到书籍');
          setOutputContent('未找到该书籍');
        }
      } catch (error) {
        console.error(error);
        setOutputContent('查询失败');
      }
    };

    // 查询全部数据
    const getAllBook = async () => {
      try {
        const book = await wrapper((store) => store.getAll())
        if (book) {
          console.log('查询结果:', book);
          setOutputContent(`总数:${book.length}`);
        } else {
          console.log('未找到书籍');
          setOutputContent('未找到该书籍');
        }
      } catch (error) {
        console.error(error);
        setOutputContent('查询失败');
      }
    };

    // 更新数据--数据不存在时会添加数据
    const updateBook = async (book) => {
      try {
        const result = await wrapper((store) => store.put(book))
        console.log(result);
        setOutputContent(`更新成功!结果: ${result}`);
      } catch (error) {
        console.error(error);
        setOutputContent('更新失败');
      }
    };

    // 删除数据
    const deleteBook = async (id) => {
      try {
        const result = await wrapper((store) => store.delete(id))
        console.log(result);
        setOutputContent(`删除成功!结果: ${result}`);
      } catch (error) {
        console.error(error);
        setOutputContent('删除失败');
      }
    };
    // 根据作者查询
    const getByAuthor = async (author) => {
      try {
        const result = await wrapper((store) => {
          const index = store.index("author_idx");
          const request = index.getAll(author);
          return request;
        })
        console.log(result);
        setOutputContent(`查询成功!结果: ${JSON.stringify(result)}`);
      } catch (error) {
        console.error(error);
        setOutputContent('查询失败');
      }
    };

    // 添加书籍
    document.getElementById('js_add_btn').onclick = () => {
      addBook({
        title: 'Web前端入门',
        author: '前端路引',
        price: (0.99 * Math.random() + 10) .toFixed(2),
      });
    };

    // 根据ID查询
    document.getElementById('js_get_by_id_btn').onclick = () => {
      const bookId = document.getElementById('js_book_id').value;
      getBook(Number(bookId));
    };

    // 根据ID删除
    document.getElementById('js_delete_by_id_btn').onclick = () => {
      const bookId = document.getElementById('js_book_id').value;
      deleteBook(Number(bookId));
    };

    // 根据ID更新
    document.getElementById('js_update_by_id_btn').onclick = () => {
      const bookId = document.getElementById('js_book_id').value;
      updateBook({
        // 主键ID
        id: Number(bookId),
        title: 'Web前端入门',
        author: '公众号:前端路引',
        price: (0.99 * Math.random() + 10) .toFixed(2),
      });
    };

    // 根据作者查询
    document.getElementById('js_get_by_author_btn').onclick = () => {
      const author = document.getElementById('js_book_author').value
      getByAuthor(author);
    };

    // 查询全部
    document.getElementById('js_get_all_btn').onclick = () => {
      getAllBook();
    };

    // 页面加载时初始化数据库
    initDB();
  })();
</script>

IndexedDB 大小限制

以下内容为 AI 回答:

85-2

获取可用的存储空间大小

navigator.storage.estimate().then((estimate) => {
  console.log(
    `已使用:`,
    (
      (estimate.usage / estimate.quota) * 100
    ).toFixed(2)
  );
  console.log(`可使用:`, (estimate.quota / 1024 / 1024).toFixed(2) + "MB");
});

相关文档:https://developer.mozilla.org/en-US/docs/Web/API/Storage_API

写在最后

由于项目中很少使用,所以这 API 给不了太多建议~~

此 API 的应用场景还是有的,可以想想下那些超大文件在线处理应用,比如 ZIP、PSD、PPT 之类的文件,可以将文件解析后存在 IndexedDB 中,在需要的时候查询指定数据即可,这样可以节约很多的解析时间。

只是需要注意,所有存在浏览器端的数据,用户清空缓存之后都将不复存在,所以在使用时需做好容错处理~~

详细文档请参阅 MDN:https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API

一个更加完善的项目:https://github.com/marco-c/eLibri