likes
comments
collection
share

Vue 存储插件的底层原理,你不知道的 localStorage API

作者站长头像
站长
· 阅读数 19

大家好,我是大家的林语冰。欢迎持续关注“前端俱乐部”,坚持阅读,一起变强

当谈到 Web 应用的客户端存储时,localStorage API 作为一种简单且得到广泛支持的解决方案脱颖而出,它允许开发者直接在用户浏览器中存储键值对。

我们在开发 Vue 等 Web 应用时,有时候需要将用户数据等状态保存在本地,实现持久化存储。

一般而言,我们自己编写存储功能,也可以使用 Pinia 生态的存储插件,比如目前人气最高的 pinia-plugin-persistedstate,而这个插件的默认存储方案就是基于 localStorage 实现的。

在本文中,我们会深度学习 localStorage API 的方方面面,包括其优缺点,以及可用于现代应用程序的其他替代存储方案。

Vue 存储插件的底层原理,你不知道的 localStorage API

免责声明

本文属于是语冰的直男翻译了属于是,略有删改,仅供粉丝参考。英文原味版请传送 Using localStorage in Modern Applications

localStorage API 是什么鬼物?

localStorage API 是 Web 浏览器的内置功能,使 Web 开发者能够在用户设备上持久存储少量数据。

localStorage 基于简单的键值对运行,允许开发者保存数字和字符串等原始数据类型。即使用户关闭浏览器或离开页面,这些数据仍然可用。localStorage 提供了一种便捷方案来维护状态和存储用户首选项,而无需依赖服务器端存储。

localStorage 基本法

localStorage 提供了多种交互方法,包括但不限于:

  • setItem
  • getItem
  • removeItem
  • clear

举个栗子,代码如下所示:

// 使用 setItem 存储数据
localStorage.setItem('username', 'john_doe')
// 使用 getItem 检索数据
const storedUsername = localStorage.getItem('username')
// 使用 removeItem 删除数据
localStorage.removeItem('username')
// 清空所有数据
localStorage.clear()

使用 JSON 序列化存储复合数据​

虽然 localStorage 擅长处理简单键值对,但它还通过 JSON 序列化支持更复杂的数据存储。利用 JSON.stringifyJSON.parse,我们可以存储和检索结构化数据,比如对象和数组。

举个栗子,代码如下所示:

const cat = {
  name: '薛定谔',
  age: 18
}

// 存储 cat 对象
localStorage.setItem('cat', JSON.stringify(cat))
// 检索并解析 cat 对象
const storedCat = JSON.parse(localStorage.getItem('cat'))

localStorage 的缺陷

尽管 localStorage 十分便捷,但它确实存在某些开发者应该注意的限制:

  • 非异步阻塞 API:一个显著的缺点在于,localStorage 作为非异步阻塞 API 运行。这意味着,localStorage 执行的任何操作都可能会阻塞主线程,降低应用程序性能和响应速度,影响用户体验。
  • 受限的数据结构:与更高级的数据库不同,localStorage 仅限于简单的键值存储。这种限制使得它不适合存储负载的数据结构,或管理数据元素之间的关系。
  • 字符串化开销localStorage 存储 JSON 数据需要先对数据字符串化,且在检索时需要先解析。这个过程会带来性能开销,可能会使操作速度减慢高达 10 倍。
  • 缺乏索引localStorage 缺乏索引功能,因此很难根据特定条件执行有效搜索或迭代数据。这个限制可能会阻碍依赖复杂数据检索的应用程序。
  • 页面阻塞:在多页面环境中,一个页面的 localStorage 操作可能会独占 CPU 资源,影响其他页面的性能。我们可以通过在两个浏览器窗口中打开测试文件,并在其中一个浏览器窗口中触发 localStorage 插入来重现 bug,然后我们会见证加载图标卡在两个窗口中。
  • 存储限制:浏览器通常对每个 localStorage 数据源施加大约 5 MiB 的存储限制。

localStorage 的优势

与对性能的担忧相反,与 IndexedDB 替代存储解决方案相比,localStorage API 出奇地快。localStorage 擅长高效处理迷你键值赋值。由于其简单性以及与浏览器的直接集成,访问和修改 localStorage 数据所产生的开销最小。

对于需要快速、简单的数据存储的场景,localStorage 仍然是一个可行的选择。

何时不适合 localStorage

虽然 localStorage 十分便捷,但它可能并不适合所有场景。

考虑以下情况,其他替代方案可能更合适:

  • 数据必须可查询:如果您的应用程序严重依赖于根据特定条件查询数据,那么 localStorage 可能无法提供必要的查询功能。复杂的数据检索可能导致代码效率低下和性能下降。
  • 大型 JSON 文档localStorage 存储大型 JSON 文档会消耗大量内存,并降低性能。评估您期望存储的数据大小,并考虑更强大的解决方案来处理大量数据集至关重要。
  • 频繁的读写操作localStorage 上过多的读写操作可能导致性能瓶颈。其他存储解决方案可能为需要频繁数据操作的应用程序提供更好的性能和可扩展性。
  • 缺乏持久性:如果您的应用无需跨会话持久数据即可运行,请考虑使用内存中数据结构,比如 Map/Set。这些选项为瞬态数据提供速度和效率。

localStorage 的替代方案

localStorage vs IndexedDB

IndexedDB 不仅可以存储键值对,还可以存储 JSON 文档。

localStorage 通常每个域名的存储限制约为 5-10MB 不同,IndexedDB 可以处理更大的数据集。而且,IndexDB 对索引的支持可以高效查询或范围查询。

但粉丝请注意,IndexedDB 缺乏可观察性,这是 localStorage 通过 storage 事件的专属功能。

此外,虽然 IndexDB 复杂查询的性能差强人意,但对于某些场景而言 IndexedDB 可能太慢。

// localStorage 通过 storage 事件监测变化
// IndexedDB 缺少监测功能
addEventListener('storage', event => {})

对于那些希望利用 IndexedDB 的全部功能的人而言,建议使用 RxDB 或 Dexie.js 等封装库。这些库通过复杂查询和可观察性等功能强化了 IndexedDB,增强了其现代应用可用性。

总而言之,IndexedDB vs localStorageIndexedDB 在处理大量数据的所有场景下都能“降维打击”,而 localStorage 在迷你键值数据集上性能更好。

文件系统 API(OPFS)

另一个知识盲区是 OPFS(源私有文件系统)。这个 API 提供对基于源的沙盒文件系统的直接访问,该文件系统针对性能高度优化,并提供对其内容的就地写入访问。

OPFS 提供了令人印象深刻的性能优势。然而,使用 OPFS API 可能十分复杂,而且能且仅能在 WebWorker 中访问

为了简化其使用并扩展其功能,请考虑使用 RxDB 的 OPFS RxStorage 等封装库,它在 OPFS API 之上构建了一个综合数据库。这种抽象允许我们利用 OPFS API 的强大功能,而无需复杂的直接使用。

localStorage vs Cookie ​

Cookie 曾经是客户端数据存储的主流方案,但由于其局限性,在现代 Web 开发中已经失宠。

虽然 Cookie 可以存储数据,但与 localStorage 相比,Cookie 的速度慢了大约 100。此外,Cookie 包含在 HTTP 请求头中,这可能会影响网络性能。

因此,不建议在当代 Web 应用中使用 Cookie 存储数据

localStorage vs WebSQL

尽管 WebSQL 为客户端数据存储提供了基于 SQL 的接口,但它是一种已废弃的技术,我们应该避免使用。

WebSQL API 已经被现代浏览器淘汰,且缺乏 IndexedDB 等替代方案的鲁棒性。

此外,WebSQL 的速度通常比 IndexedDB10 倍左右,这使得它对于需要高效数据操作和检索的应用而言不是最佳选择。

localStorage vs sessionStorage

在不需要会话外的数据持久性的场景下,开发者通常会“切换赛道”到 sessionStorage

sessionStorage 能且仅能在标签页或浏览器会话期间保留数据。它可以在页面重载和恢复后继续存在,为临时数据需求提供便捷的解决方案。

但是粉丝请注意,sessionStorage 的作用域有限,可能并不适合所有场景。

React Native 的异步存储​

对于 React Native 开发者而言,AsyncStorage API 是首选解决方案,它类似 localStorage 的镜像行为,但具有异步支持。

由于并非所有 JS 运行时都支持 localStorage,因此 AsyncStorage 为 React Native 应用中的数据持久性提供了无缝集成的替代方案。

用于 Node 的 node-localstorage

不同于浏览器,由于 Node 中不存在原生的 localStorage,因此我们会在 Node 等的运行时收到错误 ReferenceError: localStorage is not defined

node-localstorage 模块弥补了这一差距。该模块在 Node 环境中拷贝了浏览器的 localStorage,确保数据存储功能的一致性和兼容性。

浏览器扩展中的 localStorage

虽然谷歌 Chrome 和 Firefox 浏览器扩展支持 localStorage,但不建议在该上下文中使用它来存储与扩展相关的数据。在大多数场景下,浏览器都会清除数据,比如当用户清除浏览历史记录时。

相反,Extension Storage API 应该用于浏览器扩展。与 localStorage 相比,这个 API 的异步执行,且所有操作都会返回 Promise

Extension Storage 还提供自动同步功能,以便在用户登录的浏览器的所有实例之间拷贝数据。它甚至能够存储 JSON 格式的对象而不是纯字符串。

Deno 和 Bun 中的 localStorage

Deno 运行时有一个有效的 localStorage API,因此运行 localStorage.setItem() 等方法会奏效,且 localStorage 的数据会在多次运行中保留。

Bun 目前不支持 localStorage API。试试就逝世,会直接报错 ReferenceError: Can't find variable: localStorage。 要在 Bun 中本地存储数据,可以使用 bun:sqlite 模块,或者直接使用支持 Bun 的 RxDB 等 JS 数据库。

高潮总结

在现代 Web 开发领域,localStorage 是轻量级数据的存储神器,其简单性和速度使其成为迷你键值分配的最佳方案。

然而,随着应用复杂性的增加,开发者必须仔细权衡他们的存储需求。对于需要高级查询、复杂数据结构或大容量操作的场景,IndexedDB 等替代方案、RxDB 等封装库或 Deno 等运行时的 API 可以提供更强大的解决方案。

本期话题是:你有使用 localStorage 的小技巧或其他的相关存储模块吗?

欢迎在本文下方自由言论,文明共享。

如果你成功打卡读完全文,记得给自己点赞。谢谢大家的点赞,掰掰~

欢迎持续关注“前端俱乐部”,坚持阅读,一起变强

参考文献​

Vue 存储插件的底层原理,你不知道的 localStorage API

转载自:https://juejin.cn/post/7361639833863634998
评论
请登录