客户端存储 - IndexedDB

原文链接 IndexedDB for Client-Side Storage - 作者 Omri Luz

客户端存储 IndexedDB:权威指南

IndexedDB 是很强大的低级 API,用户客户端存储大量结构化数据,包括文件或者blob。不像其他的 web 存储技术,IndexedDB 允许更加复杂的搜索,关联关系,索引,为开发者提供强有力的能力,开发健壮的应用。本文将深入探索 IndexedDB 历史背景、技术复杂性、实际应用、性能考虑和真实场景应用。

历史背景

Web 存储的进化

在介绍 IndexedDB 之前,WEB 开发者原先是依赖 COOKIESLOCALSTORAGE 来做客户端的数据存储。Cookies 使用小数量的数据(通常是 4kb),面临的是大小和性能的限制。LocalStorageHTML5 引入进来的,允许存储更大的数据(通常是 5MB),但是缺少复杂查询的能力。

随着 WEB 应用的进化,对更复杂的客户端存储方案需求显而易见,尤其是随着单页面应用(SPAs)和对离线功能的需要。

IndexedDB 的发展

IndexedDB 的设计之初是为了填补现有技术留下的空白。它在 2010 年成为提案,在 2015 年被官方 W3C 推荐。它允许开发者在客户端存储重要的海量数据结构数据,支持复杂的数据类型,比如数组和对象。

IndexedDB 更新的时间线:

  • 2010:初始提案并起草
  • 2012 :在主流浏览器(比如 ChromeFirefox)上进行基本应用
  • 2015 :成为 W3C 推荐
  • 2019:性能提升和更好的错误处理

技术基础

基本概念

Key-Value 保存

IndexedDB 充当键值存储,其中数据以对象的形式存储。每个对象通过一个 key 来定义,这个键可以是一个字符串或者更复杂的键(比如字符串和数字的联合)。

对象存储

IndexedDB 中的数据被分组到对象存储中。每个对象存储可以容纳不同类型的数据结构,从而实现有序的存储和检索。对象存储可以包含简单的数据类型、数组,甚至更复杂的对象。

索引 Indexes

IndexedDB 中的 Indexes 索引提供了一种检索记录更有效的方法。使用一个索引,我们可以根据主键之外的属性查看条目,这可以大大提高读取密集型应用程序的性能。

事务 Transactions

IndexedDB 数据库的所有交互,都需要在事务上下文中执行,从而保证数据完整性。事务可以是只读的,也可以是只写的。

核心 API 结构

IndexedDB 主要围绕 indexedDB 对象展开,该对象是所有数据库操作的入口点。

打开一个数据库

javascript 复制代码
const request = indexedDB.open("myDatabase", 1);

request.onerror = (event) => {
  console.error("Database error:", error);
};

request.onsuccess = (event) => {
  const db = event.target.result;
  console.log("Database opened successfully:", db);
};

request.onupgradedneeded = (event) => {
  const db = event.target.result;
  const store = db.createObjectStore("myStore", { keyPath: "id" });
  store.createdIndex("nameIndex", "name", { unique: false });
  console.log("Object store and index created");
}

在上面的例子中,我们初始化了一个数据库,然后创建一个 myStore 对象存储,带有 ID 的关键路径,一个名为 nameIndex 的索引,能够基于 name 属性有效地查询。

完整地代码案例

IndexedDB 中地 CRUD

javacript 复制代码
// 添加数据
const addData = (db, data) => {
  const transaction = db.transaction(["myStore"], "readwrite");
  const store = transaction.objectStore("myStore");
  const request = store.add(data);
  
  request.onsuccess = () => {
    console.log("Data added: ", data);
  };
  
  request.onerror = () => {
    console.error("Error adding data: ", request.error);
  }
}

// 拉取数据
const fetchData = (db, id) => {
  const transaction = db.transaction(["myStore"], "readonly");
  const store = transaction.objectStore("myStore");
  const request = store.get(id);
  
  request.onsuccess = () => {
    console.log("Data fetched: ", request.result);
  }
  
  request.onerror = () => {
    console.error("Error fetching data: ", request.error);
  }
}

// 更新数据
const updateData = (db, updatedData) => {
  const transaction = db.transaction(["myStore"], "readwrite");
  const store = transaction.objectStore("myStore");
  const request = store.put(updatedData);
  
  request.onsuccess = () => {
    console.log("Data updated: ", updatedData);
  }
  
  request.onerror = () => {
    console.error("Error updating data: ", request.error);
  }
}

// 删除数据
const deleteData = (db, id) => {
  const transaction = db.transaction(["myStore"], "readwrite");
  const store = transaction.objectStore("myStore");
  const request = store.delete(id);
  
  request.onsuccess = () => {
    console.log("Data deleted: ", id);
  }
  
  request.onerror = () => {
    console.error("Error deleting data: ", request.error);
  }
}

高级应用:处理复杂地数据关系

考虑这么一个场景:我们需要管理一个博文应用,每个博文后面都有很多评论。使用 IndexedDB,我们可以实现更复杂的数据结构,以便我们高效查询和关联。

javascript 复制代码
// 案例 Schema:发布和评论
const schemaPost = {
  id: 1,
  title: "IndexedDB Guide",
  content: "This is an extensive guide on using IndexedDB.",
  comments: [
    { commentId: 1, text: "Great article!", timestamp: Date.now() },
    { commentId: 2, text: "Very informative.", timestamp: Date.now() }
  ]
};

const addPost = (db, post) => {
  const transaction = db.transaction(["posts"], "readwrite");
  const store = tansaction.objectStore("posts");
  const request = store.put(post);
  
  request.onsuccess = () => {
    console.log("Post added/updated: ", post);
  };
  
  request.onerror = () => {
    console.error("Error adding/updating post: ", request.error);
  };
};

const fetchComments = (db, postId) => {
 const transaction = db.transaction(["posts"], "readonly");
 const store = transaction.objectStore("posts");
 const request = store.get(postId
 
 request.onsuccess = () => {
   const post = request.result;
   console.log("Comments: ", post.comments);
 }
 
 request.onerror = () => {
   console.error("Error fetching post comments: ", request.error);
 };
};

使用 Indexes 高级查询

通过使用索引,我们可以给用户提供获取博文和评论的能力,其基于 ID 以外。这个额外的索引随着应用的迭代,可以提升性能。

javascript 复制代码
const fetchCommentsByKeyword = (db, keyword) => {
  const transaction = db.transaction(["posts"], "readonly");
  const store = transaction.objectStore("posts");
  const index = store.index("commentsIndex");
  const request = index.getAll(keyword);
  
  request.onsuccess = () => {
    console.log("Comments fetched by keyword: ", request.result)
  };
  
  request.onerror = () => {
    console.error("Error fetching comments by keyword: ", request.error);
  };
};

边缘案例和高级实现技术

  1. 处理大数据集合 :在处理大数据的时候,一个常见的陷阱是浏览器的性能。对于这些情况,请考虑从 IndexedDB 实例检索记录时,使用批量事务或者分页策略。

  2. 版本控制数据结构 :当应用程序的数据模式发生变化时,正确处理数据库的版本很重要。请确保在 onupgradeneeded 事件中提供迁移的脚本。

  3. 错误处理 :在不同的层级(open, read, write)有效处理错误,能够确保一个流畅的用户体验和更容易调试。创建一个模式来一致记录错误信息。

性能考虑和优化策略

  1. 限制事务范围:保证事务简短,并且在小范围管理数据,以提高速度。

  2. 批量操作:在单个事务中对多个操作进行分组以减少开销。

  3. 明智使用索引:为那些频繁操作的字段定义索引,以便提升查找性能。然而,需要保持平衡,因为过多的索引会减慢插入的速度。

  4. 数据库大小管理 :利用 navigator.storage.estimate() 来观测存储的用量,如果存储空间接近存储能力,则给用户一个提醒。

真实世界用户案例

  1. 离线应用 :比如像 Google Docs 应用那样,使用 IndexedDB 允许用户在断网的条件下使用。当网络恢复后同步数据。

  2. 渐进网页应用(PWAs)PWAs 通常使用 IndexedDB 来缓存 assets, 用户的偏好,和提成离线用户体验的数据。

  3. E-commerce Applications :应用程序可以使用 IndexedDB 在本地存储用户的购物车商品,无论连接如何变化都可以提供无缝体验。

  4. 游戏应用 :许多基于 web 的游戏利用 IndexedDB 来保存玩家的进度和游戏的状态,启用持久会话。

调试技术

  1. Browser DevTools :在 DevTools 中使用"应用程序"选项卡来监测数据库、对象存储和事务。我们可以检索条目,查看索引并执行手动查询。

  2. Error Logging:错误日志来捕获异常。使用集中式日志记录机制存储这些日志,方便后面分析。

  3. 单元测试:创建功能性的自动化测试,比如应用中数据输入,获取和错误处理。

总结

IndexedDB 是一款强大且灵活的客户端存储解决方案。从复杂的数据关系和索引,到优化和调试策略,有效利用 IndexedDB 需要我们了解它的架构和 API

随着浏览器能力的提升和使用模式的发展,掌握 IndexedDB对于开发现代 WEB应用的开发者来说至关重要。更多的内容,可参考官网 official IndexedDB documentation 和其他资源,比如 IndexedDB for BeginnersWeb Storage API comparison

通过利用 IndexedDB 的复杂性和强大功能,开发者可以创建出引人入胜的应用,即使在网络不稳定的情况下,也能提供丰富的数据体验,为未来的 WEB 发展奠定基础。

相关推荐
CF14年老兵几秒前
「Vue 3 + View Transition 实现炫酷圆形缩放换肤动画」
前端·css·trae
Warren983 分钟前
软件测试-Selenium学习笔记
java·javascript·笔记·学习·selenium·测试工具·安全
小璞16 分钟前
05_CursorRules_代码审查篇_Rule_code-review
前端
前端小书童17 分钟前
前端开发中的css:「ink → Bootstrap → 预处理器 → Tailwind → UnoCSS」
前端·css
萌萌哒草头将军18 分钟前
有了它 ,我彻底告别了 try-finally 🔥🔥🔥
前端·javascript·vue.js
冬至z19 分钟前
Vue 2 项目中快速集成 Jest 单元测试(超详细教程)
前端·单元测试
小璞19 分钟前
03_CursorRules_UI还原篇_Rule_ui-restoration
前端
小璞21 分钟前
01_CursorRules_需求理解篇_Rule_requirement-understanding
前端
老虎062734 分钟前
JavaWeb前端02(JavaScript)
开发语言·前端·javascript