前言
当谈到 Web 应用的客户端存储时,
localStorage
API 脱颖而出,它允许开发者直接在浏览器中存储键值对。
在开发 Vue 应用时,
我们
有时候需要将用户数据保存在本地,实现持久化存储。
我们可以自己编写存储功能,也可以使用 Pinia 生态的存储插件,比如目前人气最高的
pinia-plugin-persistedstate
,而这个插件的默认存储方案就是基于
localStorage
实现的。
在本文中,我们会深度学习
localStorage
API 的优缺点和其他替代存储方案。
localStorage API 是什么
localStorage
API 是浏览器的内置功能,使开发者能够在用户设备上持久存储少量数据。
localStorage
基于简单的键值对运行,允许开发者保存字符串等原始数据类型。即使用户关闭浏览器或离开页面,这些数据仍然可用。
localStorage
提供了一种便捷方案来维护状态和存储用户首选项,而无需依赖服务器端存储。
localStorage 基本法
localStorage
提供了多种交互方法,包括但不限于:
举个栗子,代码如下所示:
// 使用 setItem 存储数据
localStorage.setItem('username', 'john_doe')
// 使用 getItem 检索数据
const storedUsername = localStorage.getItem('username')
// 使用 removeItem 删除数据
localStorage.removeItem('username')
// 清空所有数据
localStorage.clear()
使用 JSON 序列化存储复合数据
虽然
localStorage
擅长处理简单键值对,但它还通过
JSON
序列化支持更复杂的数据存储。利用
JSON.stringify
和
JSON.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
数据源施加大约
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 可以处理更大的数据集,且其对索引的支持可以高效查询。
但粉丝请注意,
IndexedDB
缺乏可观察性,这是
localStorage
通过
storage
事件的专属功能
。
此外,虽然
IndexDB
复杂查询的性能差强人意,但对于某些场景而言
IndexedDB
可能太慢。
// localStorage 通过 storage 事件监测变化
// IndexedDB 缺少监测功能
addEventListener('storage', event => {})
对于那些希望利用
IndexedDB
的全部功能的人而言,
建议使用 RxDB 或 Dexie.js 等封装库
。这些库通过复杂查询和可观察性等功能强化了
IndexedDB
。
文件系统 API(OPFS)
另一个知识盲区是 OPFS(源私有文件系统)。这个 API 提供对基于源的沙盒文件系统的直接访问,该文件系统针对性能高度优化,并提供对其内容的就地写入访问。
OPFS 提供了令人印象深刻的性能优势。然而,使用 OPFS API 可能十分复杂,
而且能且仅能在
WebWorker
中访问
。
localStorage
vs Cookie
Cookie 曾经是客户端数据存储的主流方案,但在现代 Web 开发中已经失宠。
虽然 Cookie 可以存储数据,
但与
localStorage
相比,
Cookie
的速度慢了大约
100
倍
。此外,Cookie 包含在 HTTP 请求头中会影响网络性能。
因此,
不建议在当代 Web 应用中使用 Cookie 存储数据
。
localStorage
vs WebSQL
尽管 WebSQL 为客户端数据存储提供了基于 SQL 的接口,但它是一种已废弃的技术。
WebSQL API 已经被现代浏览器淘汰,且缺乏
IndexedDB
等替代方案的鲁棒性
。
此外,
WebSQL 的速度通常比
IndexedDB
慢
10
倍左右
,这使得它对于需要高效数据检索的应用而言不是最佳选择。
localStorage
vs
sessionStorage
在不需要会话外的数据持久性的场景下,开发者通常会“切换赛道”到
sessionStorage
。
sessionStorage
能且仅能在标签页或浏览器会话期间保留数据
。它可以在页面重载和恢复后继续存在,为临时数据需求提供便捷的解决方案。
React Native 的异步存储
对于 React Native 开发者而言,
AsyncStorage
API 是首选解决方案
,它类似
localStorage