在现代应用中使用 localStorage:一份全面的指南

在Web应用的客户端存储方面,localStorage API以其简单而广泛支持的特性脱颖而出。它允许开发人员直接在用户的浏览器中存储键值对。在本文中,我们将探讨localStorage API的各个方面,其优势、限制以及现代应用中可用的替代存储选项。

什么是localStorage API?

localStorage API是Web浏览器的内置功能,它使Web开发人员能够在用户设备上持久存储小量数据。它基于简单的键值对操作,允许开发人员保存字符串、数字和其他简单数据类型。即使在用户关闭浏览器或离开页面后,这些数据仍然可用。该API提供了一种方便的方式来维护状态并存储用户偏好,而无需依赖服务器端存储。

探索本地存储方法:一个实际例子

让我们通过一些实际的代码示例深入了解如何充分利用localStorage的强大功能。该API提供了几种交互方法,包括setItem、getItem、removeItem和clear。考虑以下代码片段:

// 使用 setItem 存储数据
localStorage.setItem('username', 'john_doe');

// 使用 getItem 检索数据
const storedUsername = localStorage.getItem('username');

// 使用 removeItem 删除数据
localStorage.removeItem('username');

// 清除所有数据
localStorage.clear();

使用JSON序列化在JavaScript中存储复杂数据

虽然localStorage在处理简单键值对方面表现出色,但它还通过JSON序列化支持更复杂的数据存储。通过使用JSON.stringify和JSON.parse,您可以存储和检索结构化数据,如对象和数组。以下是存储文档的示例:

const user = {
  name: 'Alice',
  age: 30,
  email: 'alice@example.com'
};

// 存储用户对象
localStorage.setItem('user', JSON.stringify(user));

// 检索和解析用户对象
const storedUser = JSON.parse(localStorage.getItem('user'));

了解本地存储的限制

尽管localStorage很方便,但它确实有一系列限制,开发人员应该了解:

  • 非异步阻塞API:一个重要的缺点是,js localStorage作为非异步阻塞API运行。这意味着对localStorage执行的任何操作都有可能阻塞主线程,导致应用性能变慢,用户体验不够响应。
  • 有限的数据结构:与更先进的数据库不同,localStorage受限于简单的键值存储。这种限制使其不适用于存储复杂的数据结构或管理数据元素之间的关系。
  • 字符串化开销:在localStorage中存储JSON数据需要在存储之前对数据进行字符串化,并在检索时解析。这个过程引入了性能开销,可能使操作变慢多达10倍。
  • 缺乏索引:localStorage缺乏索引功能,使得根据特定条件进行高效搜索或迭代数据变得困难。这一限制可能妨碍依赖于复杂数据检索的应用程序。
  • 标签阻塞:在多标签环境中,一个标签的localStorage操作可能通过垄断CPU资源而影响其他标签的性能。您可以通过在两个浏览器窗口中打开 此测试文件 并在其中一个窗口触发localstorage插入来重现此行为。您会观察到在两个窗口中都会卡住指示旋转器。
  • 存储限制:浏览器通常对每个源的localStorage施加约5 MiB的存储限制

仍然使用localStorage的原因

localStorage是否慢?

与对性能的担忧相反,JavaScript中的localStorage API在与IndexedDB或OPFS等替代存储解决方案相比时表现出色。它在高效处理小键值分配方面表现出色。由于其简单性并与浏览器的直接集成,访问和修改localStorage数据所产生的开销很小。对于需要快速和简单数据存储的情况,localStorage仍然是一个可行的选择。例如,RxDB在 localStorage元优化器 中使用localStorage管理简单的键值对,同时将“正常”文档存储在其他存储中,如IndexedDB。

不使用localStorage的情况

虽然localStorage提供了方便,但它可能不适用于每种情况。考虑以下情况,替代方案可能更合适:

  • 数据必须可查询:如果您的应用程序严重依赖于根据特定条件查询数据,localStorage可能无法提供必要的查询功能。复杂的数据检索可能导致效率低下和性能慢。
  • 大型JSON文档:在localStorage中存储大型JSON文档可能会占用大量内存并降低性能。评估您打算存储的数据大小,并考虑使用更强大的解决方案来处理大量数据。
  • 许多读/写操作:在localStorage上进行过多的读/写操作可能导致性能瓶颈。其他存储解决方案可能为需要频繁数据操作的应用程序提供更好的性能和可扩展性。
  • 缺乏持久性:如果您的应用程序可以在会话之间无需持久数据而运行,考虑使用诸如new Map()new Set()的内存数据结构。这些选项为瞬态数据提供了速度和效率。

JavaScript中的localStorage API的替代方案

localStorage vs IndexedDB

虽然localStorage是简单数据需求的可靠存储解决方案,但在处理更复杂需求时,探索像IndexedDB这样的替代方案是必要的。IndexedDB旨在存储不仅仅是键值对,还包括JSON文档。与localStorage通常对每个域有约5-10MB的存储限制不同,IndexedDB可以处理更大的数据集。由于支持索引,IndexedDB促进了高效查询,使范围查询成为可能。然而,值得注意的是,IndexedDB缺乏观察性,这是localStorage通过storage事件独有的功能。此外,使用IndexedDB进行复杂查询可能存在挑战,虽然其性能是可以接受的,但在某些用例中,IndexedDB可能会 太慢了

// localStorage可以通过 storage 事件观察变化。 
// 这个特性在 IndexedDB 中缺失 
addEventListener("storage", (event) => {});

对于那些希望充分利用IndexedDB并增加功能的人,建议使用类似 RxDBDexie.js 的包装库。这些库通过添加复杂查询和可观察性等功能来增强IndexedDB的可用性,提升其在现代应用中的适用性。

总结一下,当比较IndexedDB和localStorage时,IndexedDB在处理大量数据时会胜出,而localStorage在处理小键值数据集时性能更好。

文件系统API(OPFS)

另一个有趣的选择是OPFS(文件系统API)。该API提供对基于起源的、沙盒化文件系统的直接访问,高度优化以提供性能,并对其内容提供原地写访问。OPFS提供了令人印象深刻的性能优势。然而,使用OPFS API可能会复杂,并且仅在 WebWorker 内部可访问。为了简化其使用并扩展其功能,考虑使用类似 RxDB的OPFS RxStorage 的包装库,该库在OPFS API之上构建了一个全面的数据库。这种抽象允许您利用OPFS API的强大功能,而无需直接使用其复杂性。

localStorage vs Cookies

曾经是客户端数据存储的主要方法之一的Cookies由于其局限性而在现代Web开发中失去了地位。虽然它们可以存储数据,但与localStorage API相比,它们慢了大约 100倍。此外,cookies包含在HTTP头中,这可能影响网络性能。因此,在当代Web应用程序中,不建议将cookies用于数据存储。

localStorage vs WebSQL

尽管WebSQL为客户端数据存储提供了基于SQL的接口,但它是一种已弃用的技术,应该避免使用。它的API已经被现代浏览器淘汰,并且缺乏像IndexedDB这样的替代方案的健壮性。此外,WebSQL倾向于比IndexedDB慢大约10倍,使其成为对需要高效数据操作和检索的应用程序而言不太理想的选择。

localStorage vs sessionStorage

在不需要超出会话范围的数据持久性的情况下,开发人员通常会转向sessionStorage。这种存储机制仅在标签或浏览器会话的持续时间内保留数据。它可以在页面重新加载和恢复时存储数据,为临时数据需求提供了方便的解决方案。但是,需要注意的是,sessionStorage的范围有限,可能不适用于所有用例。

React Native的AsyncStorage

对于React Native开发人员,AsyncStorage API是首选解决方案,它模仿了localStorage的行为,但提供了异步支持。由于并非所有JavaScript运行时都支持localStorage,AsyncStorage为React Native应用程序中的数据持久性提供了无缝的替代方案。

Node.js的node-localstorage

由于本机的localStorage在 Node.js JavaScript运行时中不存在,因此在Node.js或像Next.js这样的基于Node的运行时中,您将收到错误 ReferenceError: localStorage is not definednode-localstorage npm包弥合了这一差距。该包在Node.js环境中复制了浏览器的localStorage API,确保了一致且兼容的数据存储功能。

浏览器扩展中的localStorage

虽然Chrome和Firefox等浏览器扩展支持localStorage API,但不建议在该上下文中使用它来存储与扩展相关的数据。在许多情况下,当用户清除浏览历史记录时,浏览器会清除数据。

相反,应该为浏览器扩展使用 Extension Storage API。与localStorage不同,storage API是async的,并且所有操作都返回Promise。它还提供自动同步,以在用户登录到浏览器的所有实例之间复制数据。storage API甚至能够存储可转换为JSON的对象,而不仅仅是纯字符串。

// 在Chrome中使用storage API

await chrome.storage.local.set({ foobar: {nr: 1} });

const result = await chrome.storage.local.get('foobar');
console.log(result.foobar); // {nr: 1}

在Deno和Bun中使用localStorage

Deno JavaScript运行时具有可用的localStorage API,因此运行 localStorage.setItem() 和其他方法将起作用,并且本地存储的数据在多次运行之间是持久的。

Bun 不支持localStorage JavaScript API。尝试使用 localStorage 将引发错误 ReferenceError: Can't find variable: localStorage。要在Bun中本地存储数据,您可以使用 bun:sqlite 模块,或直接使用Bun支持的内置JavaScript数据库,如 RxDB

结论:选择正确的存储解决方案

在现代Web开发世界中,localStorage作为轻量级数据存储的有力工具。其简单性和速度使其成为处理小键值分配的优秀选择。然而,随着应用程序复杂性的增加,开发人员必须仔细评估其存储需求。对于需要高级查询、复杂数据结构或大容量操作的情况,像IndexedDB、具有附加功能的包装库如RxDB或平台特定的API提供了更强大的解决方案。通过了解各种存储选项的优势和局限性,开发人员可以做出明智的决策,为高效且可扩展的应用程序铺平道路。

后续

2024-02-28

访问量 86

扫码关注公众号“前端微志”

第一时间获取新周刊

预览图片